1 Objective

Summary of the demographics and soil characteristics of the Rwanda long term soil health study.

1.1 Study methodology

Add in details and links on study methodology here.

library(knitr)
library(ggplot2)
library(stringr)
suppressMessages(library(dplyr))
library(sp)
suppressMessages(library(rgdal))
suppressMessages(library(dismo))
suppressMessages(library(stargazer))
library(leaflet)
library(XML)
suppressMessages(library(maptools))
library(automap)
suppressMessages(library(RStata))
suppressMessages(library(fields))
library(gstat)
library(htmltools)
suppressMessages(library(Matching))
library(reshape2)
options("RStata.StataVersion" = 12)
options("RStata.StataPath" = "/Applications/Stata/StataSE.app/Contents/MacOS/stata-se")
#chooseStataBin("/Applications/Stata/StataSE.app/Contents/MacOS/stata-se")

The working directory objects are from the original baseline analysis. I’ve updated this to pull from a stable working directory (5/4/17)

# wd <- "/Users/mlowes/drive/optimized_agronomy/soil/soil_health_study/data/rw baseline"
# dd <- paste(wd, "data", sep = "/")
# od <- paste(wd, "output", sep="/")
# md <- paste(wd, "maps", sep="/")
drive <- "~/drive/r_help/4_output/statistical_test_outputs"
od <- "output" # come back and update this later. I got about half way through?
#load data:
# This data is being drawn from the Soil lab repository. It has the baseline data with it
# d <- read.csv(paste(dd, "Rwanda_shs_commcare_soil_data_final.csv", sep="/"),  stringsAsFactors=FALSE)

2 Data Prep

2.1 Combine survey and soil data

Replicate Alex’s and Emmanuel’s merge process using “Identifiers with SSN_final” provided by Emmanuel. I use the RStata package found here

# I'm replicating Alex's do file here.
stata("merge_shs.do")
. * merge raw commcare data with soil database
. 
. * date: 11 july 2016
. 
. clear all
. set more off
. 
. * set directory
. global wd "/Users/mlowes/drive/soil health study/data/rw baseline"
. global dd "/Users/mlowes/drive/soil health study/data/rw baseline/data"
. global troubleshoot "/Users/mlowes/drive/soil health study/data/rw baseline/t
> roubleshoot"
. 
. * insheet data
. insheet using "$dd/raw_rwanda_commcare_shs.csv", clear
file /Users/mlowes/drive/soil health study/data/rw
    baseline/data/raw_rwanda_commcare_shs.csv not found
r(601);
. 
. * clean sample_id variable
. replace sample_id = "" if sample_id == "---"
. replace sample_id = "-99" if sample_id == "99"
. 
. destring sample_id, replace
. 
. * manipulate current "sample_id" to become a proper alpha-numeric unique iden
> tifier
. * this simply involves adding "C" to each numeric code that belongs to a cont
> rol farmer
. 
. * variables of interest:
.       * d_client
.       * sample_id
. 
. ren demographic_infod_client d_client
. replace d_client = "" if d_client == "---"
. destring d_client, replace
. 
. tostring sample_id, gen(sample_id_string)
. 
. replace sample_id_string = sample_id_string + "C" if d_client == 0
. drop sample_id 
. ren sample_id_string sample_id
. 
. ***** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
> ***
. * quantify and note cases where sample_id appears more than twice
.      * 39 codes appear 2x
.      * 1 code appears 3x
.      * 1 code appears 4x
.      * 1 code appears 10x
.      * 1 code appears 19x
. 
. duplicates report sample_id
. duplicates tag sample_id, gen(n_duplicate_id)
. gen d_id_problem = 1 if n_duplicate_id != 0
. replace d_id_problem = 0 if missing(d_id_problem)
. 
. ** need to investigate 5% of observations (114 samples), i.e. d_id_problem = 
> 1**
. ***** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ***
> ***
. 
. * outsheet problematic observations and examine 1-by-1
. sort sample_id
. outsheet demographicnom_enqueteur district demographic_infocellule_selected d
> emographic_infoumudugudu d_client demographic_infonom_cultivateur sample_id d
> emographic_infosex demographic_infoage_cultivateur demographic_infon_telephon
> e_cult d_id_problem using "$dd/shs_to_check.csv" if d_id_problem == 1, replac
> e comma
. 
. **************** post-investigation correction of incorrect ids *************
> ***
. 
. ren demographic_infonom_cultivateur farmer_name
. ren demographic_infocellule_selected selected_cell
. ren demographicnom_enqueteur enumerator_name
. 
. * drop test/accidentally double-entered observations
. drop if infoformid == "f138897f-6e80-4b0a-9db3-ab2134cd9e51"
. drop if farmer_name == "Mugisha"
. drop if farmer_name == "Muhunde yoramu"
. drop if farmer_name == "Test"
. drop if farmer_name == "Billy"
. drop if farmer_name == "Gakuba michel"
. drop if farmer_name == "Renata"
. drop if farmer_name == "Bub"
. drop if farmer_name == "M"
. drop if farmer_name == "Anita"
. drop if farmer_name == "Jean"
. drop if farmer_name == "Nyirazaninka lea"
. drop if farmer_name == "Mugiraneza Pacifiue"
. drop if farmer_name == "Nsengiyaremye Innocent"
. 
. * correct incorrectly recorded ids
. replace sample_id = "880C" if farmer_name == "Nyiransanzineza Marie Rose"
. replace sample_id = "811C" if farmer_name == "Ngirabatware  Daniel"
. replace sample_id = "646C" if farmer_name == "Nyirasabwa anonciata"
. replace sample_id = "624C" if farmer_name == "Nyirinkindi j Bosco"
. replace sample_id = "560C" if farmer_name == "Mubirigi Bertin"
. replace d_client = 0 if farmer_name == "Mubirigi Bertin"
. replace sample_id = "375C" if farmer_name == "Hitimana ezecquiere"
. replace sample_id = "322C" if farmer_name == "Nyirantabire arrivera"
. replace sample_id = "2566C" if farmer_name == "MUSABYIMANA Colletta"
. replace sample_id = "2399C" if farmer_name == "Nyirabajyambere anastisia"
. replace d_client = 0 if farmer_name == "Nyirabajyambere anastisia"
. replace sample_id = "2280C" if farmer_name == "Zihabake Simon"
. replace sample_id = "2202C" if farmer_name == "mugiraneza pacifique"
. replace sample_id = "1780C" if farmer_name == "Hakuzimana Theophile"
. replace sample_id = "1757C" if farmer_name == "Bavugirije Feleciane"
. replace sample_id = "1626C" if farmer_name == "Nyabivumu"
. replace sample_id = "1575C" if farmer_name == "BAHIMBANDE Beriyana."
. replace d_client = 0 if farmer_name == "BAHIMBANDE Beriyana."
. replace sample_id = "1103C" if farmer_name == "Barirwanda Elyse"
. replace sample_id = "1037C" if farmer_name == "Mukashema Faraziya"
. replace d_client = 0 if farmer_name == "Mukashema Faraziya" 
. replace sample_id = "3069" if farmer_name == "Ndababonye Silas"
. replace sample_id = "2611" if farmer_name == "Sebazungu modeste"
. replace d_client = 1 if farmer_name == "Sebazungu modeste"
. replace sample_id = "2415" if farmer_name == "Nkaka joseph"
. replace sample_id = "2412" if farmer_name == "Ndagijimana alexis"
. replace sample_id = "2410" if farmer_name == "Sebuhoro elie"
. replace sample_id = "2406" if farmer_name == "Mugisha ruth"
. replace sample_id = "2405" if farmer_name == "Mboneza hasan"
. replace sample_id = "2404" if farmer_name == "Ntibitonderwa veriane"
. replace sample_id = "2399" if farmer_name == "Nyiraburakeye feresita"
. replace sample_id = "2395" if farmer_name == "Uwimana danier"
. replace sample_id = "2394" if farmer_name == "Bizinde silas"
. replace sample_id = "2390" if farmer_name == "Nyirahabimvana keresesiya"
. replace sample_id = "2388" if farmer_name == "Nyirasekerabanzi tharcilla"
. replace sample_id = "2386" if farmer_name == "Ndererimana emmanuel"
. replace sample_id = "2291" if farmer_name == "Ndacyayisenga Feresiyani"
. replace sample_id = "2184" if farmer_name == "Muhawenimana Beretirida"
. replace sample_id = "2145" if farmer_name == "Fapfakwita celestine"
. replace sample_id = "2141" if farmer_name == "Nsangiranabo krizoroji"
. replace sample_id = "2140" if farmer_name == "Nsabyumuremyi anakereti"
. replace sample_id = "2136" if farmer_name == "Nyirantibitonderwa savoroniya"
. replace sample_id = "2131" if farmer_name == "Hagenimana Ewufroni"
. replace sample_id = "2066" if farmer_name == "Hitayezu vicent"
. replace d_client = 1 if farmer_name == "Hitayezu vicent"
. replace sample_id = "2044" if farmer_name == "Niyodushima janette"
. replace d_client = 1 if farmer_name == "Niyodushima janette"
. replace sample_id = "2024" if farmer_name == "Mukabatsinda veren A"
. replace sample_id = "2020" if farmer_name == "Sibomana innocent"
. replace sample_id = "2009" if farmer_name == "Nsabimana inyasi"
. replace sample_id = "2002" if farmer_name == "Mateso elizabet"
. replace sample_id = "1889" if farmer_name == "Niyonsaba Daniel"
. replace sample_id = "1864" if farmer_name == "Mukankubana Souzanne"
. replace sample_id = "1780" if farmer_name == "Nyirantihabose Annonciatha"
. replace sample_id = "1771" if farmer_name == "Bizimungu Damascne"
. replace sample_id = "1675" if farmer_name == "Bikorimana Isaac"
. replace sample_id = "1103" if farmer_name == "Munyabarata japhette"
. replace sample_id = "954" if farmer_name == "Hahirwabake silver"
. replace sample_id = "541" if farmer_name == "Rugemintwaza Vianney"
. replace sample_id = "454" if farmer_name == "Niyonizeye fororida"
. replace sample_id = "407" if farmer_name == "Munyaneza jean pierre"
. replace sample_id = "375" if farmer_name == "Munyarubuga nanias"
. replace sample_id = "322" if farmer_name == "Gacandaga zackarie"
. replace sample_id = "262" if farmer_name == "MUHIRWA J Damascene"
. replace sample_id = "32" if farmer_name == "Nsanzemuhire"
. 
. *** re-run duplicates test
. drop d_id_problem
. drop n_duplicate_id
. 
. duplicates report sample_id
. duplicates tag sample_id, gen(n_duplicate_id)
. gen d_id_problem = 1 if n_duplicate_id != 0
. replace d_id_problem = 0 if missing(d_id_problem)
. 
. * second try: outsheet problematic observations and examine 1-by-1
. sort sample_id
. outsheet using "$dd/shs_to_check_2.csv" if d_id_problem == 1, replace comma
. 
. * second try: correct incorrect ids
. 
. replace sample_id = "2202" if farmer_name == "Mukamurara scholastique"
. replace d_client = 1 if farmer_name == "Mukamurara scholastique"
. replace sample_id = "2195" if farmer_name == "Ndacyayisenga Feresiyani"
. replace sample_id = "824" if farmer_name == "Ngirabatware  Daniel"
. 
. replace sample_id = "" if rownumber == 238
. replace sample_id = "" if rownumber == 848
. replace sample_id = "" if rownumber == 1257
. replace sample_id = "" if rownumber == 1732
. 
. *** re-run duplicates test
. drop d_id_problem
. drop n_duplicate_id
. 
. duplicates report sample_id
. duplicates tag sample_id, gen(n_duplicate_id)
. gen d_id_problem = 1 if n_duplicate_id != 0
. replace d_id_problem = 0 if missing(d_id_problem)
. 
. * third try: outsheet problematic observations
. sort sample_id
. outsheet using "$dd/shs_to_check_3.csv" if d_id_problem == 1, replace comma
. 
. *** 4 sets of duplicates remain (2 observations)
. drop if d_id_problem == 1
. save "$dd/rwanda_commcare_shs_clean.dta", replace
. outsheet using "$dd/rwanda_commcare_shs_clean.csv", comma replace
. 
. * merge datasets
. insheet using "$dd/mne_rwanda_database_shs.csv", clear
. 
. save "$dd/mne_rwanda_database_shs.dta", replace
. 
. use "$dd/rwanda_commcare_shs_clean.dta", clear
. 
. merge 1:1 sample_id using "$dd/mne_rwanda_database_shs.dta"
. 
.    * 93 observations unmatched... :(
. 
. * investigate unmatched observations
. sort sample_id
. outsheet using "$troubleshoot/unmatched_commcare.csv" if _merge == 1, comma r
> eplace
. outsheet using "$troubleshoot/unmatched_inventory.csv" if _merge == 2, comma 
> replace
. 
. * make first round of corrections in master data
. use "$dd/rwanda_commcare_shs_clean.dta", clear
. 
. replace sample_id = "2711C" if farmer_name == "Kamuhanda Elie"
. replace sample_id = "260C" if farmer_name == "HATEGEKIMANA Mathias"
. replace sample_id = "245" if farmer_name == "Kayiranga Michel"
. replace sample_id = "1543" if farmer_name == "Ndagijimana Eliezel"
. replace sample_id = "1543C" if farmer_name == "Mukantanganzwa odeta"
. replace sample_id = "1632" if farmer_name == "Nyiranziguye Cecile"
. replace sample_id = "2782" if farmer_name == "Mukamungu leocadie"
. replace sample_id = "421C" if farmer_name == "Niyonizeye Francine"
. replace sample_id = "828C" if farmer_name == "Mukamazimpaka Terese"
. replace sample_id = "364" if farmer_name == "Mukamuyango verina"
. replace sample_id = "364C" if farmer_name == "Mujawimana jeanne"
. replace sample_id = "368" if farmer_name == "Nyiramanzi jean damascene"
. replace sample_id = "368C" if farmer_name == "Karwiyegura rachel"
. replace sample_id = "391" if farmer_name == "Mugemane augistin"
. replace sample_id = "391C" if farmer_name == "Ngirishuti sumayire"
. replace sample_id = "395" if farmer_name == "Niyomugabo amiel"
. replace sample_id = "395C" if farmer_name == "Ntawiniga augustin"
. replace sample_id = "396" if farmer_name == "Mpayimana philippe"
. replace sample_id = "329" if farmer_name == "Mucyezangango emmanuel"
. replace sample_id = "411" if farmer_name == "Nsabimana mathias"
. replace sample_id = "411C" if farmer_name == "Singirankabo"
. replace sample_id = "414" if farmer_name == "Rutaharama eldephonse"
. replace sample_id = "329C" if farmer_name == "Mukamana josephine"
. replace sample_id = "657" if farmer_name == "Nyandwi vincent"
. replace sample_id = "569C" if farmer_name == "Uwimana Bercilla"
. replace sample_id = "2051C" if farmer_name == "Ntambabazi Anastase"
. replace sample_id = "2249" if farmer_name == "Uwineza valentine"
. replace sample_id = "2360C" if farmer_name == "Bamporineza j deDieu"
. replace sample_id = "2001" if farmer_name == "Nyiramakuba thanene"
. replace sample_id = "2897C" if farmer_name == "Mukagatanazi Marianne"
. replace sample_id = "2965C" if farmer_name == "Twizeyimana Theoneste"
. 
. drop if farmer_name == "Yjk"
. drop if farmer_name == "Uwambajimana Agnes"
. drop if farmer_name == "Tr"
. 
. replace sample_id = "2415C" if farmer_name == "Nyiramahirwe Frolance"
. replace sample_id = "851" if farmer_name == "Bigirimana  Amon"
. 
. 
. * duplicates check
. drop d_id_problem
. drop n_duplicate_id
. 
. duplicates report sample_id
. duplicates tag sample_id, gen(n_duplicate_id)
. gen d_id_problem = 1 if n_duplicate_id != 0
. replace d_id_problem = 0 if missing(d_id_problem)
. 
. * second try: merge
. merge 1:1 sample_id using "$dd/mne_rwanda_database_shs.dta"
. 
. * deal with 30 unmatched cases
. sort sample_id
. outsheet using "$troubleshoot/unmatched_commcare_2.csv" if _merge == 1, comma
>  replace
. outsheet using "$troubleshoot/unmatched_inventory_2.csv" if _merge == 2, comm
> a replace
. 
. * 5 submitted surveys with no corresponding sample
. *drop if _merge == 1
. 
. * 19 samples with no corresponding survey
. *drop if _merge == 2
. 
. * outsheet merged database
. outsheet using "$dd/rwanda_shs_baseline_data.csv", comma replace
. * THIS IS THE CLEAN VERSION! ONLY USE THIS! DON'T USE OTHER THINGS!
. 

Import the results of Alex’s do file.

d <- read.csv("data/rwanda_shs_baseline_data.csv", stringsAsFactors = F)
Identifiers <- read.csv("data/Identifiers with SSN_final.csv", stringsAsFactors = F)
wetChem <- read.csv(paste("/Users/mlowes/drive/jupyter/Rwanda Acidity", "Original chem_rwanda_shs.csv", sep = "/"))
d <- left_join(d, Identifiers, by="sample_id")
# now import the soil results and merge with survey data
soilDir <- normalizePath(file.path("..", "..", "OAF Soil Lab Folder", "Projects", "rw_shs_baseline", "4_predicted", "other_summaries"))
soilRF <- read.csv(file=paste(soilDir, "combined-predictions-including-bad-ones.csv", sep="/"))
orgSoilNames <- read.csv("data/rwshs_rfresults.csv", stringsAsFactors = F) #old from original variable work
names(orgSoilNames)[names(orgSoilNames)=="X"] <- "SSN"

Match new soil model outputs to variable names from original analysis in 2016 and join soil variables with survey data

soil <- soilRF %>% dplyr::select(SSN, Calcium, Magnesium, pH, Total.Nitrogen, Organic.Carbon, Exchangeable.Aluminium) %>%
  rename(
    m3.Ca = Calcium,
    m3.Mg = Magnesium,
    Total.N = Total.Nitrogen,
    Total.C = Organic.Carbon,
    ExAl = Exchangeable.Aluminium
  ) %>% mutate(
    SSN = as.character(SSN)
  )
d <- left_join(d, soil, by="SSN")

2.2 Cleaning baseline variables

Now let’s start cleaning the demographic variables

d[d=="---"] <- NA
names(d) <- gsub("text_final_questions", "", names(d))
names(d) <- gsub("intro_champ_echantillon", "", names(d))
names(d) <- gsub("demographic_info", "", names(d))
names(d) <- gsub("other_inputs_", "", names(d))
names(d) <- gsub("crop1_15b_inputs", "", names(d))
names(d) <- gsub("crop2_15b_inputs", "", names(d))
names(d) <- gsub("^15b", "", names(d))
names(d) <- gsub("historical_intro", "", names(d))
names(d)[names(d)=="field_dim"] <- "field_dim1"
names(d)[names(d)=="v51"] <- "field_dim2"

Address unusual field sizes

ggplot(d, aes(x=field_dim1, y=field_dim2)) + geom_point()

It seems really unlikely that fields are 200 meters long while only being 20 meters wide. I don’t know how to check this though.

# clean field dimensions here - winsor the values to something reasonable.
d[(d$field_dim1>=100 | d$field_dim2>=100) & !is.na(d$field_dim1), c("field_dim1", "field_dim2")]

Take care of demographic data formatting issues

# deal with names and drop unnecessary variables
d <- d %>% 
    dplyr::select(-c(rownumber, infoformid, introductiond_accept, photo,
        infocompleted_time, 
        enumerator_name, contains("phone"), farmer_name, farmersurname, farmername,
        d_respondent, additionalsamplepackedandsenttol, additionalsamplerequestedfromlab,
        datedryingcompleteifnecessary, driedindistrictifnecessary, senttohqyo,
        collectedindistrictyo, excessstoredathq_, receivedathq_,dateofinitialdryingifnecessary,
        samplecollectedinfieldyo, field_des, samplewetordry)) %>%
    rename(
    female = sex,
    age = age_cultivateur,
    own = d_own,
    client = d_client) %>%
    mutate(
    female= ifelse(female=="gore", 1,0),
    field.size = field_dim1*field_dim2
    )
d$total.seasons <- apply(d[, grep("d_season_list", names(d))], 1, function(x) {
    sum(x, na.rm=T)})

Clean agronomic practice variables

agVars <- c("n_season_fert", "n_season_compost", "n_season_lime", "n_season_fallow",
            "n_seasons_leg_1", "n_seasons_leg_2")
summary(d[,agVars])
 n_season_fert    n_season_compost n_season_lime     n_season_fallow   n_seasons_leg_1  n_seasons_leg_2   
 Min.   : 0.000   Min.   : 0.000   Min.   : 0.0000   Min.   : 0.0000   Min.   : 0.000   Min.   : -88.000  
 1st Qu.: 0.000   1st Qu.: 2.000   1st Qu.: 0.0000   1st Qu.: 0.0000   1st Qu.: 0.000   1st Qu.:   0.000  
 Median : 1.000   Median : 5.000   Median : 0.0000   Median : 0.0000   Median : 1.000   Median :   2.000  
 Mean   : 2.041   Mean   : 5.651   Mean   : 0.1819   Mean   : 0.6355   Mean   : 2.171   Mean   :   4.368  
 3rd Qu.: 3.000   3rd Qu.:10.000   3rd Qu.: 0.0000   3rd Qu.: 0.0000   3rd Qu.: 4.000   3rd Qu.:   5.000  
 Max.   :10.000   Max.   :10.000   Max.   :10.0000   Max.   :10.0000   Max.   :10.000   Max.   :3333.000  
 NA's   :19       NA's   :19       NA's   :19        NA's   :19        NA's   :19       NA's   :19        

Sort out the legumes as a second crop

table(d$n_seasons_leg_2, useNA = 'ifany')

 -88    0    1    2    3    4    5    6    7    8    9   10   15   22   50   53   83   88  101  102 3333 <NA> 
   1  956  221  258  174  130  301   61   34   66   57  199    1    1    1    3    1    1    1    1    1   19 
d$n_seasons_leg_2 <- ifelse(d$n_seasons_leg_2 <0 | d$n_seasons_leg_2>10, NA, d$n_seasons_leg_2)

2.2.1 Check out client variable - why are there missing values?

table(d$client, useNA = 'ifany')

   0    1 <NA> 
1233 1236   19 
d[is.na(d$client), c("sample_id")]
 [1] "1189C" "1207C" "1295"  "1295C" "1298C" "1300"  "1300C" "1366C" "1476C" "1485C" "1554"  "1573"  "1614"  "2042"  "2371C" "2373"  "2375" 
[18] "2382"  "2442" 
# replace client based on whether there is a C in the client variable.
d$client.check <- ifelse(grepl("C", d$sample_id)==T, 0, 1)
table(d$client, d$client.check)
   
       0    1
  0 1233    0
  1    1 1235

It looks like most farmers were recorded correctly except for one farmer who was coded as Tubura farmer but their sample_id indicate their a control. Let’s take a look:

d[d$client==1 & d$client.check==0 & !is.na(d$d_gps), c("sample_id", "tuburacontroltc")]
# they should be a control
d[d$client==1 & d$client.check==0 & !is.na(d$d_gps), "client"] <- 0
# remove farmers for which we have soil data but no survey data (using)
d <- d[-which(grepl("using", d$X_merge)),]
table(d$client, d$client.check, useNA = 'ifany')
   
       0    1
  0 1234    0
  1    0 1235
# update client to equal client check
d$client <- d$client.check

Fix some more variable names:

names(d)[names(d)=="field_kg_fert1_1"] <- "field_kg_fert1_15b"
names(d)[names(d)=="field_kg_fert2_1"] <- "field_kg_fert2_15b"
names(d)[names(d)=="field_kg_compost"] <- "field_kg_compost_15b"

Recode variables to numeric:

# recode to numeric
varlist <- c("client", "own", "crop1_15b_seedkg", "crop1_15b_yield", "crop1_15b_yield_",
    "crop2_15b_seedkg", "crop2_15b_yield", "crop2_15b_yield_", "field_kg_fert1_15b",
    "field_kg_fert2_15b", "field_kg_compost_15b", "d_lime_15b", "kg_lime_15b")
# check that there aren't values hidden in the character variables
#apply(d[,varlist], 2, function(x){table(x, useNA='ifany')})
# recode characters to numerics
d[, varlist] <- sapply(d[,varlist], as.numeric)

2.2.2 Sort out kgs of lime applied

table(d$kg_lime_15b, useNA = 'ifany')

 -88    1    3    4    5   10   13   15   20   25   30   35   50   60   88  100  150  200 <NA> 
   1    2    2    2    6    5    1    1    2    5    1    1    8    1    2    2    4    1 2422 
d$kg_lime_15b <- ifelse(abs(d$kg_lime_15b)==88, NA, d$kg_lime_15b)
# divide out GPS coordinates
# http://rfunction.com/archives/1499
# replace the blank gps_pic_guide with info
d <- cbind(d, str_split_fixed(d$gps_pic_guid, " ", n=4))
names(d)[names(d)=="1" |names(d)== "2" | names(d)== "3" | names(d)== "4"] <- c("lat", "lon", "alt", "precision")
d[,c("lat", "lon", "alt", "precision")] <- sapply(d[,c("lat", "lon", "alt", "precision")],
                                                  function(x){as.numeric(as.character(x))})

2.3 Cleaning soil data

Cleaning of soil data: Come back, check and clean the soil data before outputting to clean data set. Plot each of the soil variables to look for unrealistic values.

dim(d[is.na(d$m3.Ca),])
[1]  30 111
d <- d[-which(is.na(d$m3.Ca)),]
       
summary(d[,c("m3.Ca", "m3.Mg", "pH", "Total.N", "Total.C", "ExAl")])
     m3.Ca            m3.Mg               pH           Total.N          Total.C            ExAl        
 Min.   :  90.8   Min.   :  39.45   Min.   :3.840   Min.   :0.0600   Min.   :0.8749   Min.   :0.01578  
 1st Qu.: 358.0   1st Qu.: 121.32   1st Qu.:4.995   1st Qu.:0.1300   1st Qu.:1.7290   1st Qu.:0.13532  
 Median : 620.5   Median : 184.74   Median :5.470   Median :0.1500   Median :2.0301   Median :0.30884  
 Mean   : 821.3   Mean   : 209.82   Mean   :5.483   Mean   :0.1521   Mean   :2.0791   Mean   :0.59145  
 3rd Qu.:1102.5   3rd Qu.: 262.09   3rd Qu.:5.950   3rd Qu.:0.1700   3rd Qu.:2.3466   3rd Qu.:0.93700  
 Max.   :6090.4   Max.   :1052.13   Max.   :8.140   Max.   :0.3500   Max.   :4.0658   Max.   :2.50849  

2.3.1 Soil Type

names(d)[names(d)=="general_field_infosoil_type"] <- "soil_type"
names(d)[names(d)=="general_field_infotexture_soil"] <- "soil_texture"
d$black_soil <- ifelse(d$soil_type=="black", 1,0)
d$red_soil <- ifelse(d$soil_type=="red", 1,0)
d$sandy_soil <-ifelse(d$soil_type=="sandy", 1,0)

Let’s check out the rows for which we don’t have soil data and drop them as they won’t contribute to the full picture.

2.3.2 Graphs of RW baseline soil variables

soilVars <- c("m3.Ca", "m3.Mg", "pH", "Total.N", "Total.C", "ExAl")
for(i in 1:length(soilVars)){
print(
  ggplot(data=d, aes(x=as.factor(client), y=d[,soilVars[i]])) + 
    geom_boxplot() +
    labs(x="Tubura Farmer", y=soilVars[i], title = paste("RW baseline soil - ", soilVars[i], sep = ""))
  )  
}

2.3.3 Check out soil relationships

There are biologically predictable relationships between soil chemical characteristics. For instance, we expect Ca and Mg to move in the same direction and be positively correlated with pH. If we had Aluminum as an outcome, we’d expect pH to be negatively correlated with soluable aluminum. Let’s look quickly to confirm if those relationships are present:

ggplot(d, aes(x=m3.Ca, y=m3.Mg)) + geom_point() +
    stat_smooth(method="loess") +
    labs(x = "Calcium (m3)", y= "Magnesium (m3)", title="Calcium and Magnesium relationship")

ggplot(d, aes(x=pH, y=m3.Ca)) + geom_point() +
  stat_smooth(method="loess") +
  labs(x = "pH", y="Calcium (m3)", title = "pH and Calcium relationship")

ggplot(d, aes(x=pH, y=m3.Mg)) + geom_point() +
  stat_smooth(method="loess") +
  labs(x = "pH", y="Magnesium (m3)", title = "pH and Magnesium relationship")

ggplot(d, aes(x=pH, y=ExAl)) + geom_point() +
  stat_smooth(method="loess") +
  labs(x = "pH", y="Exchangeable Aluminum", title = "pH and Aluminum relationship")

ggplot(d, aes(x=Total.C, y=Total.N)) + geom_point() + 
  stat_smooth(method="loess") +
  labs(x = "Total Carbon", y="Total Nitrogen", title = "Carbon and Nitrogen relationship")

The soil characteristics are moving in the manner that is consistent with our understanding of soil chemical processes.

Save clean demographic and soil data to external file

write.csv(d, file="data/shs rw baseline.csv")
save(d, file="data/shs rw baseline.Rdata")

2.3.4 Map of baseline observations

Produce a simple map of where our observations are

See here for more on using markerClusterOptions in leaflet.

In the map below, the larger green circles are Tubura farmers and the smaller blue circles are control farmers.

e <- d[!is.na(d$lon),]
ss <- SpatialPointsDataFrame(coords = e[, c("lon", "lat")], data=e)
pal <- colorNumeric(c("navy", "green"), domain=unique(ss$client))
map <- leaflet() %>% addTiles() %>%
  setView(lng=rwanda$longitude, lat=rwanda$latitude, zoom=8) %>%
  addCircleMarkers(lng=ss$lon, lat=ss$lat, 
                   radius= ifelse(ss$client==1, 10,6),
                   color = pal(ss$client),
clusterOptions = markerClusterOptions(disableClusteringAtZoom=13, spiderfyOnMaxZoom=FALSE))
map

3 Summary statistics

3.0.1 Table of final baseline breakdown

count <- d %>% group_by(district) %>% 
  dplyr::summarize(
    t.count = sum(ifelse(client==1,1,0)),
    c.count = sum(ifelse(client==0,1,0)),
    total = n()
  ) %>% ungroup()
count <- as.data.frame(count)
write.csv(count, file="data/final rw sample breakdown.csv", row.names=F)
#as.data.frame(count)

3.1 Baseline balance

Let’s see how balanced our farmers are in terms of demographic variables. Tubura farmers were selected based on (list criteria) and control farmers in the same area tha fit the same criteria were also selected. No matching process has been performed to identify the control farmers that most closely resemble the Tubura farmers in the sample. These results are entirely reflecting the balance inherent in the identification process, not any statistical matching of treatment and control.

d$valley <- ifelse(d$general_field_infofield_location=="valley", 1,0)
d$hillside <- ifelse(d$general_field_infofield_location=="hillside", 1,0)
d$hilltop <- ifelse(d$general_field_infofield_location=="hilltop", 1,0)
names(d)[names(d)=="general_field_infograde_hill"] <- "slope"
cor(d[, grep("betail_", names(d))], use='complete.obs')
                       betail_ownedn_inka betail_ownedn_ihene betail_ownedn_inkoko betail_ownedn_ingurube betail_ownedn_intama
betail_ownedn_inka             1.00000000         0.035195326           0.09700986            0.021287801          0.040572776
betail_ownedn_ihene            0.03519533         1.000000000           0.12626563           -0.009531107         -0.002990226
betail_ownedn_inkoko           0.09700986         0.126265627           1.00000000            0.049825561          0.025680156
betail_ownedn_ingurube         0.02128780        -0.009531107           0.04982556            1.000000000          0.020300946
betail_ownedn_intama           0.04057278        -0.002990226           0.02568016            0.020300946          1.000000000
names(d)[grep("betail_", names(d))] <- c("cows", "goats", "chickens", "pigs", "sheep")
d$own.cows <- ifelse(d$cows>0 & !is.na(d$cows), 1,0)
d$own.goats <- ifelse(d$goats>0 & !is.na(d$goats), 1,0)
d$own.chickens <- ifelse(d$chickens>0 & !is.na(d$chickens), 1,0)
d$own.pigs <- ifelse(d$pigs>0 & !is.na(d$pigs), 1,0)
d$own.sheep <- ifelse(d$sheep>0 & !is.na(d$sheep), 1,0)

3.2 Crop selection in 15B

names(d)[names(d)=="inputuse_15b_priorculture_15b_1"] <-"crop_15b"
d$crop_15b <- tolower(d$crop_15b)
sort(prop.table(table(d$crop_15b, useNA = 'ifany')))

         kor         hwag        sheke       coffee          cer        uburo         teke       insina         shaz          nyo          gan 
0.0004100041 0.0008200082 0.0008200082 0.0012300123 0.0024600246 0.0036900369 0.0041000410 0.0057400574 0.0086100861 0.0131201312 0.0196801968 
   other_veg          ray          yum          gor       ntacyo         soya          jum          big         saka          shy 
0.0209102091 0.0524805248 0.0586305863 0.0594505945 0.0594505945 0.0791307913 0.0914309143 0.1599015990 0.1648216482 0.1931119311 
d$climbing_bean <- ifelse(d$crop_15b=="shy", 1,0)
d$sorghum <- ifelse(d$crop_15b=="saka", 1,0)
d$bush_bean <- ifelse(d$crop_15b=="big", 1,0)
d$maize <- ifelse(d$crop_15b=="gor", 1,0)
out.list <- c("female", "age", "hhsize", "own", "field.size",
              "n_season_fert", "n_season_compost", "n_season_lime", "n_season_fallow", "n_seasons_leg_1", "n_seasons_leg_2", "slope",  "alt", "valley", "hillside", "hilltop", "cows", "goats", "chickens", "pigs", "sheep", "climbing_bean", "sorghum", "bush_bean","maize", soilVars, "own.cows", "own.goats", "own.chickens", "own.pigs", "own.sheep", "black_soil", "red_soil", "sandy_soil")
output <- do.call(rbind, lapply(out.list, function(x) {
  
  out <- t.test(d[,x] ~ d[,"client"], data=d)
  tab <- data.frame(out[[5]][[2]],out[[5]][[1]], out[3])
  tab[,1:2] <- round(tab[,1:2],3)
  names(tab) <- c(names(out[[5]]), "pvalue")
  return(tab)
}))
# use p.adjust with bonferroni correction
output$pvalue <- p.adjust(output$pvalue, method="fdr")
rownames(output) <- out.list
output <- output[order(output$pvalue),]
output$pvalue <- ifelse(output[, 3] < 0.001, "< 0.001", round(output[, 3], 3)) 
colnames(output) <- c("Tubura Client","Non-Tubura", "p-value")  
print(kable(output))
Tubura Client Non-Tubura p-value
n_season_fert 3.270 0.835 < 0.001
female 0.494 0.649 < 0.001
own.cows 0.681 0.542 < 0.001
age 44.020 48.070 < 0.001
hhsize 5.418 4.897 < 0.001
n_seasons_leg_2 2.533 3.086 < 0.001
own.chickens 0.340 0.267 < 0.001
own.pigs 0.355 0.281 < 0.001
n_season_lime 0.227 0.132 < 0.001
pigs 0.525 0.394 0.006
chickens 1.217 0.898 0.01
hillside 0.682 0.733 0.02
own 0.931 0.956 0.027
maize 0.072 0.047 0.027
valley 0.267 0.224 0.03
cows 1.174 0.919 0.03
sorghum 0.146 0.184 0.03
sheep 0.212 0.144 0.041
climbing_bean 0.212 0.174 0.041
own.sheep 0.105 0.078 0.046
n_season_fallow 0.699 0.566 0.083
n_season_compost 5.803 5.524 0.124
m3.Ca 798.870 843.860 0.143
pH 5.464 5.502 0.209
field.size 667.682 606.312 0.29
ExAl 0.604 0.578 0.404
n_seasons_leg_1 2.220 2.133 0.568
hilltop 0.051 0.044 0.568
bush_bean 0.154 0.166 0.568
m3.Mg 207.955 211.689 0.568
Total.C 2.072 2.087 0.568
slope 10.236 10.460 0.615
Total.N 0.152 0.153 0.615
sandy_soil 0.144 0.149 0.823
black_soil 0.537 0.543 0.835
alt 1570.722 1565.840 0.841
goats 1.074 1.057 0.841
red_soil 0.280 0.276 0.841
own.goats 0.483 0.486 0.892

Pre-PSM table 1 summary

table1vars <- c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg")
table1 <- output[rownames(output) %in% table1vars, ]
write.csv(table1, file=paste("output","rw table1.csv", sep = "/"), row.names=T)
#write table
write.csv(output, file=paste("output", "baseline balance.csv", sep="/"), row.names=T)
# write baseline balance table for ES per his table layouts
t4order <- c("age", "female", "hhsize", "own")
table4vars <- paste(t4order, collapse="|")
rw.table4 <- output[grepl(table4vars, rownames(output)),]
rw.table4 <- rw.table4[order(match(rownames(rw.table4), t4order)),]
write.csv(rw.table4, file=paste("output", "pre match balance table4.csv", sep="/"),
          row.names = T)
t5order <- c("field.size", "slope", "alt", "hilltop", "hillside", "valley",
                      "climbing_bean", "sorghum", "bush_bean", "maize")
table5vars <- paste(t5order,collapse="|")
rw.table5 <- output[grepl(table5vars, rownames(output)),]
rw.table5 <- rw.table5[order(match(rownames(rw.table5), t5order)),]
write.csv(rw.table5, file=paste("output", "pre match balance table5.csv", sep = "/"),
          row.names = T)
t6order <- c("own.cows", "cows", "own.pigs", "pigs", "own.sheep", "sheep", "own.goats", "goats","own.chickens", "chickens")
table6vars <- paste(t6order, collapse = "|")
rw.table6 <- output[grep(table6vars, rownames(output)), ]
rw.table6 <- rw.table6[order(match(rownames(rw.table6), t6order)),]
write.csv(rw.table6, file=paste("output", "pre match balance table6.csv", sep = "/"), 
          row.names=T)

3.3 Overall balance interpretation

Demographic variables We are not well balanced along the main demographic variables we collected, sex, age and HH size. For the purposes of inference we can test some matching algorithms to improve the match between Tubura and control farmers.

Agricultural practice variables We are decently balanced along agricultural practice variables. Our course of action here is similiar to our options with the demographic variables.

Soil Variables We are balanced on the primary soil variables of interest betwen our Tubura farmers and the comparison farmers.

3.4 Baseline balance by district

dist.output <- do.call(rbind, lapply(split(d, d$district), function(x) {
  
  tab <- do.call(rbind, lapply(out.list, function(y) {
    
    out <- t.test(x[,y] ~ x[,"client"], data=x)
    tab <- data.frame(out[[5]][[2]],out[[5]][[1]], out[3])
    tab[,1:2] <- round(tab[,1:2],3)
    names(tab) <- c(names(out[[5]]), "pvalue")
    #tab[,3] <- p.adjust(tab[,3], method="holm")
    #tab[,3] <- ifelse(tab[,3] < 0.001, "< 0.001", round(tab[,3],3))
    #print(tab)
    return(tab)
  }))
  
  return(data.frame(district = unique(x$district), tab))
}))
rownames(dist.output) <- NULL
dist.output$variable <- rep(out.list,length(unique(d$district)))    
# order variables 
dist.output <- dist.output[, c(1, 5, 2:4)]
dist.output$pvalue <- p.adjust(dist.output$pvalue, method="fdr")
dist.output <- dist.output[order(dist.output$pvalue),]
dist.output$pvalue <- ifelse(dist.output$pvalue < 0.001, "< 0.001", round(dist.output$pvalue,3))
colnames(dist.output) <- c("District", "Varible", "Tubura Client","Non-Tubura", "p-value")  
print(kable(dist.output))
District Varible Tubura Client Non-Tubura p-value
162 Karongi n_season_fert 3.809 0.449 < 0.001
240 Mugonero n_season_fert 4.313 0.592 < 0.001
435 Rutsiro n_season_fert 4.200 1.072 < 0.001
318 Nyamasheke n_season_fert 5.000 1.500 < 0.001
474 Rwamagana n_season_fert 2.723 1.064 < 0.001
123 Huye n_season_fert 2.268 0.322 < 0.001
461 Rutsiro own.cows 0.824 0.542 < 0.001
45 Gatsibo_NLWH n_season_fert 1.500 0.296 < 0.001
279 Nyamagabe n_season_fert 4.000 1.582 < 0.001
132 Huye hillside 0.696 0.922 < 0.001
131 Huye valley 0.259 0.052 < 0.001
320 Nyamasheke n_season_lime 0.322 0.041 < 0.001
201 Kayonza n_season_fert 2.093 0.776 0.001
470 Rwamagana age 44.009 51.882 0.002
157 Karongi female 0.463 0.671 0.005
393 Nyaruguru hhsize 6.217 4.717 0.008
110 Gisagara own.cows 0.696 0.326 0.008
315 Nyamasheke hhsize 5.698 4.719 0.008
112 Gisagara own.chickens 0.370 0.065 0.009
113 Gisagara own.pigs 0.565 0.217 0.012
95 Gisagara cows 1.043 0.413 0.018
119 Huye age 43.491 49.400 0.021
274 Nyamagabe female 0.482 0.764 0.038
337 Nyamasheke maize 0.081 0.007 0.038
352 Nyanza female 0.234 0.543 0.038
357 Nyanza n_season_fert 1.298 0.152 0.038
97 Gisagara chickens 1.196 0.174 0.039
2 Gatsibo_LWH age 41.931 50.138 0.042
41 Gatsibo_NLWH age 40.023 46.210 0.042
40 Gatsibo_NLWH female 0.326 0.556 0.044
502 Rwamagana own.chickens 0.411 0.227 0.052
242 Mugonero n_season_lime 0.176 0.023 0.06
43 Gatsibo_NLWH own 0.907 1.000 0.061
469 Rwamagana female 0.554 0.736 0.063
196 Kayonza female 0.407 0.672 0.067
149 Huye own.cows 0.670 0.487 0.071
237 Mugonero hhsize 5.802 5.031 0.074
432 Rutsiro hhsize 5.624 4.867 0.08
1 Gatsibo_LWH female 0.241 0.483 0.084
181 Karongi maize 0.086 0.019 0.084
236 Mugonero age 44.290 49.331 0.091
305 Nyamagabe own.cows 0.732 0.491 0.105
319 Nyamasheke n_season_compost 6.517 5.418 0.106
365 Nyanza valley 0.298 0.087 0.107
366 Nyanza hillside 0.702 0.913 0.107
334 Nyamasheke climbing_bean 0.416 0.274 0.109
310 Nyamagabe black_soil 0.446 0.218 0.11
62 Gatsibo_NLWH sorghum 0.279 0.469 0.116
391 Nyaruguru female 0.391 0.652 0.121
437 Rutsiro n_season_lime 0.327 0.096 0.138
134 Huye cows 1.161 0.774 0.144
224 Kayonza Total.N 0.179 0.194 0.15
256 Mugonero climbing_bean 0.321 0.192 0.159
363 Nyanza slope 7.362 9.239 0.159
491 Rwamagana sorghum 0.188 0.327 0.159
396 Nyaruguru n_season_fert 2.130 1.065 0.174
81 Gisagara hhsize 5.348 4.326 0.179
98 Gisagara pigs 0.609 0.304 0.179
137 Huye pigs 0.964 0.748 0.179
152 Huye own.pigs 0.759 0.617 0.179
324 Nyamasheke slope 14.315 17.185 0.179
426 Nyaruguru own.sheep 0.109 0.000 0.19
276 Nyamagabe hhsize 5.482 4.673 0.206
6 Gatsibo_LWH n_season_fert 2.552 1.500 0.217
450 Rutsiro sheep 0.467 0.247 0.217
120 Huye hhsize 5.134 4.574 0.223
140 Huye sorghum 0.241 0.374 0.223
500 Rwamagana own.cows 0.545 0.400 0.227
245 Mugonero n_seasons_leg_2 3.131 4.077 0.231
128 Huye n_seasons_leg_2 3.732 4.860 0.243
50 Gatsibo_NLWH n_seasons_leg_2 2.977 3.889 0.244
292 Nyamagabe chickens 0.857 0.309 0.244
463 Rutsiro own.chickens 0.291 0.193 0.254
87 Gisagara n_season_fallow 0.500 0.087 0.255
89 Gisagara n_seasons_leg_2 2.370 3.283 0.255
191 Karongi own.pigs 0.401 0.291 0.255
307 Nyamagabe own.chickens 0.286 0.127 0.255
457 Rutsiro pH 5.275 5.396 0.255
59 Gatsibo_NLWH pigs 0.244 0.062 0.262
207 Kayonza slope 3.296 1.690 0.275
322 Nyamasheke n_seasons_leg_1 2.309 1.733 0.275
355 Nyanza own 0.915 1.000 0.275
441 Rutsiro slope 15.521 14.030 0.276
362 Nyanza n_seasons_leg_2 2.085 3.478 0.277
472 Rwamagana own 0.911 0.973 0.285
445 Rutsiro hilltop 0.097 0.042 0.293
84 Gisagara n_season_fert 1.087 0.348 0.3
420 Nyaruguru Total.C 2.056 2.176 0.3
164 Karongi n_season_lime 0.099 0.013 0.31
275 Nyamagabe age 43.768 49.018 0.31
485 Rwamagana cows 1.062 0.518 0.31
108 Gisagara Total.C 1.836 1.967 0.316
225 Kayonza Total.C 2.355 2.520 0.316
411 Nyaruguru sheep 0.196 0.000 0.316
96 Gisagara goats 1.522 0.935 0.319
477 Rwamagana n_season_fallow 1.214 0.718 0.319
382 Nyanza ExAl 0.168 0.262 0.324
503 Rwamagana own.pigs 0.277 0.173 0.324
487 Rwamagana chickens 1.580 0.964 0.329
370 Nyanza chickens 2.298 1.174 0.331
172 Karongi hilltop 0.068 0.025 0.341
303 Nyamagabe Total.C 2.282 2.160 0.341
347 Nyamasheke own.pigs 0.409 0.308 0.341
407 Nyaruguru cows 1.196 0.891 0.343
92 Gisagara valley 0.283 0.130 0.346
203 Kayonza n_season_lime 0.537 0.707 0.35
455 Rutsiro m3.Ca 568.202 643.814 0.35
294 Nyamagabe sheep 0.286 0.091 0.352
46 Gatsibo_NLWH n_season_compost 2.547 3.420 0.353
226 Kayonza ExAl 0.125 0.162 0.364
233 Kayonza red_soil 0.204 0.086 0.364
348 Nyamasheke own.sheep 0.060 0.021 0.367
142 Huye maize 0.062 0.017 0.374
197 Kayonza age 42.093 46.500 0.379
55 Gatsibo_NLWH hilltop 0.012 0.062 0.384
216 Kayonza sheep 0.037 0.172 0.384
221 Kayonza m3.Ca 1685.356 1998.563 0.384
422 Nyaruguru own.cows 0.826 0.674 0.397
79 Gisagara female 0.435 0.609 0.403
425 Nyaruguru own.pigs 0.609 0.435 0.403
173 Karongi cows 1.383 1.127 0.404
312 Nyamagabe sandy_soil 0.089 0.200 0.407
410 Nyaruguru pigs 0.783 0.457 0.407
262 Mugonero pH 5.075 5.177 0.43
375 Nyanza bush_bean 0.255 0.413 0.436
266 Mugonero own.cows 0.786 0.700 0.441
105 Gisagara m3.Mg 218.396 247.896 0.446
223 Kayonza pH 6.111 6.242 0.446
260 Mugonero m3.Ca 458.124 521.824 0.446
390 Nyanza sandy_soil 0.298 0.457 0.449
158 Karongi age 44.741 47.285 0.453
180 Karongi bush_bean 0.074 0.127 0.453
353 Nyanza age 45.128 50.152 0.453
333 Nyamasheke sheep 0.161 0.027 0.464
379 Nyanza pH 6.049 5.875 0.464
323 Nyamasheke n_seasons_leg_2 2.101 2.660 0.469
424 Nyaruguru own.chickens 0.283 0.152 0.478
446 Rutsiro cows 1.442 1.048 0.478
460 Rutsiro ExAl 0.807 0.706 0.493
33 Gatsibo_LWH own.goats 0.552 0.414 0.493
185 Karongi Total.N 0.138 0.133 0.493
74 Gatsibo_NLWH own.pigs 0.128 0.062 0.496
104 Gisagara m3.Ca 920.553 1100.991 0.496
153 Huye own.sheep 0.054 0.017 0.496
436 Rutsiro n_season_compost 8.176 7.639 0.496
293 Nyamagabe pigs 1.000 0.800 0.499
438 Rutsiro n_season_fallow 0.473 0.289 0.499
265 Mugonero ExAl 0.928 0.814 0.516
103 Gisagara maize 0.043 0.000 0.516
250 Mugonero hilltop 0.015 0.000 0.516
297 Nyamagabe bush_bean 0.000 0.036 0.516
377 Nyanza m3.Ca 1119.490 966.731 0.516
398 Nyaruguru n_season_lime 0.043 0.000 0.516
507 Rwamagana sandy_soil 0.018 0.000 0.516
174 Karongi goats 0.883 1.070 0.518
52 Gatsibo_NLWH alt 1012.158 1129.172 0.521
102 Gisagara bush_bean 0.217 0.348 0.521
118 Huye female 0.500 0.591 0.521
199 Kayonza own 0.963 0.897 0.521
231 Kayonza own.sheep 0.037 0.103 0.521
349 Nyamasheke black_soil 0.537 0.616 0.521
430 Rutsiro female 0.545 0.620 0.521
109 Gisagara ExAl 0.349 0.256 0.525
371 Nyanza pigs 0.085 0.217 0.525
386 Nyanza own.pigs 0.064 0.152 0.525
419 Nyaruguru Total.N 0.153 0.161 0.525
117 Gisagara sandy_soil 0.239 0.370 0.53
309 Nyamagabe own.sheep 0.179 0.091 0.531
11 Gatsibo_LWH n_seasons_leg_2 1.983 2.737 0.531
32 Gatsibo_LWH own.cows 0.500 0.379 0.531
58 Gatsibo_NLWH chickens 1.674 1.000 0.531
66 Gatsibo_NLWH m3.Mg 237.170 258.718 0.531
93 Gisagara hillside 0.587 0.717 0.531
209 Kayonza valley 0.556 0.431 0.531
214 Kayonza chickens 1.000 0.466 0.531
220 Kayonza maize 0.019 0.069 0.531
283 Nyamagabe n_seasons_leg_1 1.625 1.145 0.531
383 Nyanza own.cows 0.660 0.522 0.531
394 Nyaruguru own 0.848 0.935 0.531
465 Rutsiro own.sheep 0.212 0.157 0.531
479 Rwamagana n_seasons_leg_2 1.324 1.700 0.531
488 Rwamagana pigs 0.509 0.318 0.531
17 Gatsibo_LWH cows 0.759 0.552 0.554
163 Karongi n_season_compost 7.068 6.557 0.554
159 Karongi hhsize 5.185 4.905 0.559
314 Nyamasheke age 45.738 47.938 0.559
111 Gisagara own.goats 0.630 0.500 0.56
136 Huye chickens 1.795 1.348 0.56
160 Karongi own 0.957 0.981 0.56
211 Kayonza hilltop 0.093 0.172 0.563
339 Nyamasheke m3.Mg 167.529 152.133 0.567
311 Nyamagabe red_soil 0.464 0.582 0.567
127 Huye n_seasons_leg_1 1.393 1.043 0.571
143 Huye m3.Ca 812.314 882.646 0.574
447 Rutsiro goats 0.515 0.663 0.582
480 Rwamagana slope 3.705 4.273 0.582
107 Gisagara Total.N 0.142 0.149 0.586
76 Gatsibo_NLWH black_soil 0.616 0.704 0.592
106 Gisagara pH 5.706 5.842 0.6
171 Karongi hillside 0.784 0.835 0.602
18 Gatsibo_LWH goats 1.345 1.000 0.608
19 Gatsibo_LWH chickens 1.052 0.690 0.608
146 Huye Total.N 0.134 0.137 0.608
219 Kayonza bush_bean 0.222 0.138 0.608
298 Nyamagabe maize 0.089 0.036 0.608
389 Nyanza red_soil 0.106 0.043 0.608
442 Rutsiro alt 1945.122 1896.140 0.608
462 Rutsiro own.goats 0.273 0.331 0.608
71 Gatsibo_NLWH own.cows 0.570 0.481 0.61
374 Nyanza sorghum 0.298 0.196 0.61
268 Mugonero own.chickens 0.382 0.315 0.62
154 Huye black_soil 0.509 0.583 0.627
64 Gatsibo_NLWH maize 0.093 0.049 0.637
213 Kayonza goats 1.759 1.397 0.637
344 Nyamasheke own.cows 0.638 0.575 0.638
47 Gatsibo_NLWH n_season_lime 0.058 0.025 0.641
228 Kayonza own.goats 0.685 0.586 0.641
234 Kayonza sandy_soil 0.037 0.086 0.641
57 Gatsibo_NLWH goats 1.198 1.654 0.66
3 Gatsibo_LWH hhsize 5.362 5.741 0.66
4 Gatsibo_LWH own 0.983 1.000 0.66
56 Gatsibo_NLWH cows 0.895 0.741 0.66
78 Gatsibo_NLWH sandy_soil 0.279 0.210 0.66
88 Gisagara n_seasons_leg_1 2.304 2.870 0.66
115 Gisagara black_soil 0.587 0.478 0.66
121 Huye own 0.973 0.991 0.66
125 Huye n_season_lime 0.036 0.113 0.66
184 Karongi pH 5.423 5.481 0.66
186 Karongi Total.C 1.898 1.851 0.66
200 Kayonza field.size 572.833 423.776 0.66
217 Kayonza climbing_bean 0.000 0.017 0.66
235 Mugonero female 0.656 0.715 0.66
238 Mugonero own 0.947 0.915 0.66
259 Mugonero maize 0.000 0.008 0.66
350 Nyamasheke red_soil 0.315 0.260 0.66
368 Nyanza cows 1.170 0.891 0.66
376 Nyanza maize 0.128 0.065 0.66
395 Nyaruguru field.size 570.022 495.239 0.66
414 Nyaruguru bush_bean 0.065 0.022 0.66
415 Nyaruguru maize 0.022 0.000 0.66
434 Rutsiro field.size 584.327 429.614 0.66
443 Rutsiro valley 0.152 0.193 0.66
449 Rutsiro pigs 0.315 0.241 0.66
489 Rwamagana sheep 0.500 0.364 0.66
493 Rwamagana maize 0.027 0.009 0.66
251 Mugonero cows 1.328 1.169 0.664
456 Rutsiro m3.Mg 163.400 173.586 0.664
178 Karongi climbing_bean 0.407 0.354 0.664
218 Kayonza sorghum 0.167 0.103 0.669
8 Gatsibo_LWH n_season_lime 0.500 0.328 0.678
37 Gatsibo_LWH black_soil 0.172 0.241 0.678
38 Gatsibo_LWH red_soil 0.638 0.552 0.678
53 Gatsibo_NLWH valley 0.756 0.691 0.678
83 Gisagara field.size 493.957 559.261 0.678
135 Huye goats 0.955 0.809 0.678
145 Huye pH 5.557 5.616 0.678
167 Karongi n_seasons_leg_2 3.174 3.558 0.678
182 Karongi m3.Ca 735.109 793.491 0.678
183 Karongi m3.Mg 238.490 256.902 0.678
239 Mugonero field.size 441.359 391.208 0.678
290 Nyamagabe cows 0.911 0.727 0.678
325 Nyamasheke alt 1676.658 1702.940 0.678
351 Nyamasheke sandy_soil 0.114 0.082 0.678
385 Nyanza own.chickens 0.532 0.435 0.678
409 Nyaruguru chickens 0.652 0.391 0.678
421 Nyaruguru ExAl 0.851 0.956 0.678
454 Rutsiro maize 0.176 0.217 0.678
5 Gatsibo_LWH field.size 776.345 919.724 0.688
492 Rwamagana bush_bean 0.366 0.309 0.688
48 Gatsibo_NLWH n_season_fallow 0.744 0.543 0.694
91 Gisagara alt 1036.055 923.525 0.694
187 Karongi ExAl 0.631 0.573 0.694
308 Nyamagabe own.pigs 0.732 0.655 0.694
313 Nyamasheke female 0.644 0.692 0.7
332 Nyamasheke pigs 0.779 0.582 0.7
418 Nyaruguru pH 5.049 4.957 0.7
150 Huye own.goats 0.518 0.461 0.706
141 Huye bush_bean 0.196 0.243 0.706
423 Nyaruguru own.goats 0.348 0.435 0.711
31 Gatsibo_LWH ExAl 0.174 0.203 0.714
405 Nyaruguru hillside 0.957 0.913 0.714
406 Nyaruguru hilltop 0.043 0.087 0.714
101 Gisagara sorghum 0.435 0.522 0.72
254 Mugonero pigs 0.412 0.331 0.722
340 Nyamasheke pH 5.136 5.089 0.727
155 Huye red_soil 0.179 0.139 0.729
34 Gatsibo_LWH own.chickens 0.345 0.276 0.738
73 Gatsibo_NLWH own.chickens 0.302 0.247 0.738
189 Karongi own.goats 0.494 0.538 0.743
270 Mugonero own.sheep 0.122 0.092 0.752
403 Nyaruguru alt 1791.131 1814.738 0.752
63 Gatsibo_NLWH bush_bean 0.174 0.222 0.754
364 Nyanza alt 1508.930 1529.554 0.754
504 Rwamagana own.sheep 0.223 0.182 0.754
10 Gatsibo_LWH n_seasons_leg_1 4.345 4.690 0.756
80 Gisagara age 44.761 46.848 0.756
126 Huye n_season_fallow 0.661 0.487 0.756
133 Huye hilltop 0.045 0.026 0.756
138 Huye sheep 0.062 0.035 0.756
194 Karongi red_soil 0.327 0.367 0.756
215 Kayonza pigs 0.315 0.224 0.756
269 Mugonero own.pigs 0.244 0.285 0.756
272 Mugonero red_soil 0.252 0.292 0.756
278 Nyamagabe field.size 310.929 283.782 0.756
291 Nyamagabe goats 0.750 0.618 0.756
429 Nyaruguru sandy_soil 0.109 0.065 0.756
475 Rwamagana n_season_compost 4.009 3.691 0.756
72 Gatsibo_NLWH own.goats 0.512 0.568 0.757
124 Huye n_season_compost 5.232 4.878 0.757
176 Karongi pigs 0.531 0.449 0.757
161 Karongi field.size 492.262 448.278 0.76
384 Nyanza own.goats 0.617 0.543 0.76
388 Nyanza black_soil 0.574 0.500 0.76
354 Nyanza hhsize 5.213 4.891 0.763
360 Nyanza n_season_fallow 1.149 0.870 0.769
499 Rwamagana ExAl 0.348 0.320 0.779
20 Gatsibo_LWH pigs 0.138 0.207 0.787
60 Gatsibo_NLWH sheep 0.047 0.025 0.787
227 Kayonza own.cows 0.370 0.310 0.787
229 Kayonza own.chickens 0.185 0.138 0.787
230 Kayonza own.pigs 0.222 0.172 0.787
317 Nyamasheke field.size 791.856 620.884 0.787
427 Nyaruguru black_soil 0.630 0.696 0.787
467 Rutsiro red_soil 0.315 0.349 0.787
473 Rwamagana field.size 918.893 837.927 0.787
26 Gatsibo_LWH m3.Ca 1281.010 1186.023 0.788
42 Gatsibo_NLWH hhsize 5.349 5.160 0.788
147 Huye Total.C 1.869 1.895 0.788
244 Mugonero n_seasons_leg_1 1.336 1.177 0.788
282 Nyamagabe n_season_fallow 0.268 0.182 0.788
378 Nyanza m3.Mg 242.303 230.433 0.788
433 Rutsiro own 0.915 0.934 0.788
85 Gisagara n_season_compost 4.326 3.957 0.791
70 Gatsibo_NLWH ExAl 0.202 0.220 0.793
44 Gatsibo_NLWH field.size 1319.302 1459.901 0.796
468 Rutsiro sandy_soil 0.176 0.151 0.796
408 Nyaruguru goats 0.609 0.739 0.805
486 Rwamagana goats 1.768 1.582 0.805
23 Gatsibo_LWH sorghum 0.276 0.328 0.805
372 Nyanza sheep 0.021 0.043 0.805
387 Nyanza own.sheep 0.021 0.043 0.805
496 Rwamagana pH 5.697 5.735 0.805
27 Gatsibo_LWH m3.Mg 253.408 241.489 0.812
36 Gatsibo_LWH own.sheep 0.034 0.017 0.812
399 Nyaruguru n_season_fallow 0.370 0.261 0.812
24 Gatsibo_LWH bush_bean 0.397 0.345 0.815
82 Gisagara own 0.826 0.870 0.815
277 Nyamagabe own 0.964 0.982 0.819
373 Nyanza climbing_bean 0.043 0.022 0.819
271 Mugonero black_soil 0.527 0.492 0.821
431 Rutsiro age 43.685 44.614 0.821
498 Rwamagana Total.C 2.161 2.188 0.821
501 Rwamagana own.goats 0.536 0.573 0.821
90 Gisagara slope 8.391 7.739 0.823
9 Gatsibo_LWH n_season_fallow 0.397 0.293 0.834
190 Karongi own.chickens 0.407 0.437 0.835
338 Nyamasheke m3.Ca 504.693 482.970 0.836
28 Gatsibo_LWH pH 6.055 6.005 0.837
188 Karongi own.cows 0.765 0.741 0.839
206 Kayonza n_seasons_leg_2 1.111 1.259 0.839
122 Huye field.size 599.607 567.922 0.845
205 Kayonza n_seasons_leg_1 2.019 2.155 0.845
281 Nyamagabe n_season_lime 0.500 0.400 0.845
412 Nyaruguru climbing_bean 0.196 0.239 0.845
39 Gatsibo_LWH sandy_soil 0.172 0.207 0.851
77 Gatsibo_NLWH red_soil 0.035 0.049 0.851
192 Karongi own.sheep 0.080 0.095 0.851
210 Kayonza hillside 0.352 0.397 0.851
253 Mugonero chickens 1.076 0.954 0.851
286 Nyamagabe alt 2124.389 2110.970 0.851
288 Nyamagabe hillside 0.821 0.855 0.851
296 Nyamagabe sorghum 0.036 0.055 0.851
301 Nyamagabe pH 4.838 4.869 0.851
306 Nyamagabe own.goats 0.464 0.418 0.851
397 Nyaruguru n_season_compost 5.696 6.043 0.851
481 Rwamagana alt 990.930 956.133 0.851
505 Rwamagana black_soil 0.652 0.682 0.851
246 Mugonero slope 15.939 16.462 0.853
267 Mugonero own.goats 0.542 0.569 0.862
400 Nyaruguru n_seasons_leg_1 2.043 2.348 0.862
482 Rwamagana valley 0.402 0.373 0.862
151 Huye own.chickens 0.420 0.391 0.865
289 Nyamagabe hilltop 0.054 0.036 0.865
343 Nyamasheke ExAl 1.006 1.037 0.868
13 Gatsibo_LWH alt 1094.532 1135.001 0.872
316 Nyamasheke own 0.926 0.938 0.873
483 Rwamagana hillside 0.536 0.564 0.873
476 Rwamagana n_season_lime 0.348 0.318 0.887
75 Gatsibo_NLWH own.sheep 0.035 0.025 0.894
284 Nyamagabe n_seasons_leg_2 3.161 2.982 0.894
356 Nyanza field.size 838.926 766.217 0.894
361 Nyanza n_seasons_leg_1 2.511 2.283 0.894
466 Rutsiro black_soil 0.461 0.440 0.894
144 Huye m3.Mg 215.213 211.384 0.894
336 Nyamasheke bush_bean 0.087 0.075 0.894
129 Huye slope 6.616 6.817 0.903
490 Rwamagana climbing_bean 0.036 0.027 0.903
177 Karongi sheep 0.167 0.196 0.905
195 Karongi sandy_soil 0.025 0.019 0.908
264 Mugonero Total.C 2.147 2.171 0.912
280 Nyamagabe n_season_compost 7.857 7.655 0.912
300 Nyamagabe m3.Mg 115.173 112.262 0.913
212 Kayonza cows 0.685 0.828 0.913
249 Mugonero hillside 0.855 0.869 0.913
67 Gatsibo_NLWH pH 6.011 5.989 0.917
222 Kayonza m3.Mg 347.261 354.791 0.917
304 Nyamagabe ExAl 1.150 1.119 0.918
464 Rutsiro own.pigs 0.194 0.181 0.926
94 Gisagara hilltop 0.130 0.152 0.93
116 Gisagara red_soil 0.152 0.130 0.93
416 Nyaruguru m3.Ca 415.882 434.099 0.93
35 Gatsibo_LWH own.pigs 0.121 0.103 0.932
444 Rutsiro hillside 0.752 0.765 0.934
7 Gatsibo_LWH n_season_compost 5.672 5.879 0.936
401 Nyaruguru n_seasons_leg_2 3.178 3.370 0.937
471 Rwamagana hhsize 4.920 5.000 0.938
241 Mugonero n_season_compost 6.229 6.100 0.938
345 Nyamasheke own.goats 0.430 0.445 0.938
287 Nyamagabe valley 0.125 0.109 0.946
478 Rwamagana n_seasons_leg_1 1.652 1.582 0.946
175 Karongi chickens 1.463 1.386 0.949
232 Kayonza black_soil 0.685 0.707 0.949
257 Mugonero sorghum 0.061 0.054 0.949
428 Nyaruguru red_soil 0.261 0.239 0.954
29 Gatsibo_LWH Total.N 0.149 0.148 0.956
54 Gatsibo_NLWH hillside 0.233 0.247 0.956
130 Huye alt 1649.310 1655.403 0.956
148 Huye ExAl 0.290 0.298 0.956
170 Karongi valley 0.148 0.139 0.956
295 Nyamagabe climbing_bean 0.161 0.145 0.956
342 Nyamasheke Total.C 2.242 2.256 0.956
381 Nyanza Total.C 1.763 1.747 0.956
198 Kayonza hhsize 5.074 5.155 0.956
139 Huye climbing_bean 0.089 0.096 0.96
156 Huye sandy_soil 0.268 0.278 0.96
193 Karongi black_soil 0.580 0.570 0.96
243 Mugonero n_season_fallow 0.863 0.908 0.96
247 Mugonero alt 1796.175 1787.253 0.96
261 Mugonero m3.Mg 162.889 165.293 0.96
299 Nyamagabe m3.Ca 329.187 335.265 0.96
330 Nyamasheke goats 0.899 0.870 0.96
331 Nyamasheke chickens 0.591 0.562 0.96
341 Nyamasheke Total.N 0.162 0.161 0.96
358 Nyanza n_season_compost 3.191 3.326 0.96
369 Nyanza goats 1.234 1.283 0.96
417 Nyaruguru m3.Mg 146.273 144.478 0.96
440 Rutsiro n_seasons_leg_2 2.036 2.091 0.96
448 Rutsiro chickens 0.879 0.964 0.96
458 Rutsiro Total.N 0.149 0.150 0.96
494 Rwamagana m3.Ca 1230.875 1244.512 0.96
506 Rwamagana red_soil 0.330 0.318 0.96
21 Gatsibo_LWH sheep 0.103 0.086 0.971
252 Mugonero goats 1.321 1.292 0.973
439 Rutsiro n_seasons_leg_1 3.388 3.440 0.974
30 Gatsibo_LWH Total.C 1.915 1.906 0.999
65 Gatsibo_NLWH m3.Ca 1198.728 1210.672 0.999
166 Karongi n_seasons_leg_1 2.438 2.468 0.999
179 Karongi sorghum 0.148 0.152 0.999
202 Kayonza n_season_compost 3.481 3.534 0.999
204 Kayonza n_season_fallow 0.463 0.483 0.999
255 Mugonero sheep 0.221 0.231 0.999
321 Nyamasheke n_season_fallow 0.651 0.671 0.999
326 Nyamasheke valley 0.154 0.151 0.999
346 Nyamasheke own.chickens 0.215 0.219 0.999
495 Rwamagana m3.Mg 268.544 267.492 0.999
12 Gatsibo_LWH slope 4.259 4.224 1
14 Gatsibo_LWH valley 0.603 0.603 1
15 Gatsibo_LWH hillside 0.397 0.397 1
49 Gatsibo_NLWH n_seasons_leg_1 1.547 1.556 1
51 Gatsibo_NLWH slope 4.058 4.099 1
68 Gatsibo_NLWH Total.N 0.159 0.159 1
69 Gatsibo_NLWH Total.C 2.009 2.012 1
86 Gisagara n_season_lime 0.022 0.022 1
99 Gisagara sheep 0.022 0.022 1
100 Gisagara climbing_bean 0.022 0.022 1
114 Gisagara own.sheep 0.022 0.022 1
165 Karongi n_season_fallow 0.840 0.848 1
168 Karongi slope 12.006 11.975 1
169 Karongi alt 1807.818 1806.996 1
208 Kayonza alt 1203.816 1202.646 1
248 Mugonero valley 0.130 0.131 1
258 Mugonero bush_bean 0.176 0.177 1
263 Mugonero Total.N 0.145 0.145 1
273 Mugonero sandy_soil 0.176 0.177 1
285 Nyamagabe slope 12.571 12.618 1
302 Nyamagabe Total.N 0.153 0.153 1
327 Nyamasheke hillside 0.819 0.822 1
328 Nyamasheke hilltop 0.027 0.027 1
329 Nyamasheke cows 1.242 1.267 1
380 Nyanza Total.N 0.126 0.126 1
392 Nyaruguru age 48.457 48.304 1
402 Nyaruguru slope 9.478 9.457 1
413 Nyaruguru sorghum 0.304 0.304 1
451 Rutsiro climbing_bean 0.327 0.331 1
453 Rutsiro bush_bean 0.012 0.012 1
459 Rutsiro Total.C 2.187 2.187 1
484 Rwamagana hilltop 0.062 0.064 1
497 Rwamagana Total.N 0.182 0.182 1
16 Gatsibo_LWH hilltop 0.000 0.000 NA
22 Gatsibo_LWH climbing_bean 0.000 0.000 NA
25 Gatsibo_LWH maize 0.000 0.000 NA
61 Gatsibo_NLWH climbing_bean 0.000 0.000 NA
335 Nyamasheke sorghum 0.000 0.000 NA
359 Nyanza n_season_lime 0.000 0.000 NA
367 Nyanza hilltop 0.000 0.000 NA
404 Nyaruguru valley 0.000 0.000 NA
452 Rutsiro sorghum 0.000 0.000 NA

3.5 District balance interpretation

Demographic variables interpretation here.

Agricultural practice variables interpretation here

Soil Variables interpretation here

write.csv(dist.output, file=paste("output", "district balance.csv", sep="/"), row.names=T)

3.6 Baseline balance by Tubura tenure

Look at farmers by duration of tenure farming with Tubura. We want to understand, at least with an initial naive baseline sense, what is the cumulative effect of Tubura practices on soil health outcomes?

We will look only at current Tubura farmers and compare first year farmers to farmers with more experience with Tubura.

oafOnly <- d[which(d$client==1 & d$total.seasons>=1),]
nTenure <- oafOnly %>% group_by(total.seasons) %>% 
  dplyr::summarize(
    n = n()
  ) %>% ungroup() %>% as.data.frame()
nTenure$val <- paste(nTenure$total.seasons, " (", "n = ", nTenure$n, ")", sep = "")
for(i in 1:length(soilVars)){
print(
  ggplot(oafOnly, aes(x=as.factor(total.seasons), y=oafOnly[,soilVars[i]])) + 
    geom_boxplot() +
    scale_x_discrete(labels=nTenure$val) + 
    theme(legend.position = "bottom", axis.text.x = element_text(angle = 45, hjust = 1)) +
    labs(x="Tubura Tenure", y=soilVars[i], title = paste("RW baseline soil by tenure - ", soilVars[i], sep = ""))
  )  
}

3.7 Tenure summaries

tenureSum <- aggregate(oafOnly[,out.list], by=list(oafOnly$total.seasons), function(x){
  round(mean(x, na.rm=T),2)
})
tenureSum <- as.data.frame(t(tenureSum))
colnames(tenureSum) <- c(paste(seq(1,14,1), " seas.", sep = ""))
print(kable(tenureSum))
1 seas. 2 seas. 3 seas. 4 seas. 5 seas. 6 seas. 7 seas. 8 seas. 9 seas. 10 seas. 11 seas. 12 seas. 13 seas. 14 seas.
Group.1 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00 13.00 14.00
female 0.43 0.48 0.60 0.49 0.58 0.59 0.54 0.50 0.65 0.60 0.75 0.54 0.55 0.48
age 42.52 42.77 44.33 45.68 43.07 43.96 43.71 50.91 51.85 42.67 48.25 49.00 52.73 49.52
hhsize 5.23 5.19 5.08 5.22 6.07 5.58 6.11 5.66 6.65 6.13 6.58 5.51 4.55 6.12
own 0.93 0.95 0.92 0.95 0.98 0.97 1.00 0.93 0.85 0.97 0.92 0.90 0.91 0.95
field.size 570.34 634.79 630.65 554.01 486.51 484.79 412.07 592.95 330.00 644.30 617.83 444.85 280.82 679.71
n_season_fert 1.78 2.33 2.63 3.29 4.67 4.90 5.93 5.59 6.00 5.63 7.25 7.08 7.18 6.79
n_season_compost 5.36 5.33 4.81 6.36 6.84 7.13 8.36 7.43 7.30 7.33 8.67 8.15 8.27 7.81
n_season_lime 0.17 0.27 0.36 0.31 0.22 0.26 0.07 0.07 0.20 0.27 0.08 0.15 0.09 0.48
n_season_fallow 0.64 0.50 0.77 0.91 0.67 0.66 0.64 0.77 1.10 0.47 0.42 0.23 1.82 0.67
n_seasons_leg_1 1.99 2.04 2.10 2.44 2.93 2.89 1.93 2.91 1.60 1.77 2.92 3.54 2.09 3.02
n_seasons_leg_2 2.47 2.48 2.03 2.34 2.71 2.62 4.75 3.50 2.75 2.50 3.92 1.45 2.82 2.12
slope 9.07 8.71 9.65 8.80 9.58 11.84 14.29 11.77 13.60 12.07 14.17 12.00 14.91 14.38
alt 1536.57 1491.09 1550.41 1516.91 1650.84 1582.27 1758.60 1639.74 1812.98 1755.70 1788.95 1644.45 1702.35 1633.76
valley 0.32 0.32 0.27 0.26 0.31 0.22 0.25 0.23 0.00 0.10 0.33 0.18 0.09 0.21
hillside 0.62 0.62 0.71 0.71 0.67 0.74 0.71 0.66 0.95 0.87 0.67 0.79 0.82 0.76
hilltop 0.06 0.06 0.03 0.03 0.02 0.05 0.04 0.11 0.05 0.03 0.00 0.03 0.09 0.02
cows 0.97 1.00 0.94 1.49 1.93 1.28 1.29 1.09 3.25 1.27 1.75 1.00 1.00 1.07
goats 0.99 1.11 1.00 1.27 1.40 1.00 0.75 1.16 1.20 0.87 0.83 1.08 0.45 1.26
chickens 1.08 1.19 1.08 1.21 1.31 0.87 1.71 1.61 1.20 1.40 3.58 1.72 0.45 1.00
pigs 0.54 0.52 0.58 0.52 0.38 0.47 0.46 0.55 0.45 0.60 0.25 0.82 0.64 0.83
sheep 0.20 0.21 0.29 0.35 0.18 0.16 0.86 0.23 0.15 0.40 0.00 0.05 0.18 0.17
climbing_bean 0.14 0.18 0.21 0.17 0.20 0.32 0.50 0.36 0.35 0.43 0.33 0.38 0.55 0.38
sorghum 0.17 0.17 0.12 0.14 0.11 0.06 0.04 0.02 0.05 0.10 0.17 0.05 0.09 0.00
bush_bean 0.14 0.17 0.17 0.17 0.16 0.19 0.04 0.18 0.15 0.03 0.08 0.10 0.18 0.14
maize 0.06 0.07 0.06 0.08 0.09 0.08 0.11 0.11 0.10 0.10 0.08 0.03 0.00 0.05
m3.Ca 825.38 808.59 892.40 829.36 938.66 801.09 731.73 692.21 447.86 551.92 604.23 813.76 556.78 544.69
m3.Mg 211.62 199.21 226.45 211.18 230.07 217.20 207.29 222.62 143.91 183.00 209.25 281.07 158.64 177.75
pH 5.47 5.50 5.49 5.46 5.59 5.48 5.41 5.42 5.06 5.21 5.33 5.46 5.27 5.19
Total.N 0.15 0.16 0.16 0.16 0.15 0.15 0.14 0.13 0.16 0.15 0.13 0.13 0.15 0.15
Total.C 2.07 2.10 2.09 2.09 2.17 2.05 2.04 1.88 2.33 2.11 1.92 1.92 2.06 2.10
ExAl 0.58 0.59 0.63 0.60 0.50 0.65 0.67 0.56 1.01 0.81 0.68 0.54 0.81 0.81
own.cows 0.69 0.63 0.58 0.74 0.71 0.69 0.82 0.70 0.90 0.87 0.75 0.64 0.64 0.81
own.goats 0.46 0.52 0.47 0.46 0.53 0.50 0.46 0.55 0.50 0.47 0.50 0.51 0.27 0.52
own.chickens 0.30 0.35 0.33 0.30 0.33 0.30 0.54 0.43 0.45 0.43 0.58 0.41 0.45 0.33
own.pigs 0.42 0.37 0.32 0.32 0.27 0.33 0.39 0.36 0.35 0.37 0.17 0.38 0.36 0.40
own.sheep 0.13 0.09 0.14 0.17 0.11 0.08 0.29 0.14 0.10 0.13 0.00 0.03 0.09 0.07
black_soil 0.54 0.54 0.55 0.56 0.49 0.43 0.54 0.45 0.55 0.50 0.25 0.62 0.45 0.62
red_soil 0.30 0.25 0.29 0.25 0.29 0.37 0.25 0.30 0.30 0.33 0.42 0.23 0.36 0.26
sandy_soil 0.13 0.19 0.14 0.15 0.18 0.18 0.07 0.18 0.05 0.07 0.17 0.13 0.18 0.10

3.8 Tenure balance

We’re defining Tubura tenure as having 3 or more seasons of experience farming with Tubura. We draw the line at 3 seasons as three seasons of fertilizer use is approximately when we’d expect fertilizer to start to have a detrimental effect on soil health.

oafOnly$tenured <- ifelse(oafOnly$total.seasons>=3,1,0)
tenure <- do.call(rbind, lapply(out.list, function(x) {
  
  out <- t.test(oafOnly[,x] ~ oafOnly[,"tenured"], data=oafOnly)
  tab <- data.frame(out[[5]][[1]], out[[5]][[2]], out[3])
  tab[,1:2] <- round(tab[,1:2],3)
  names(tab) <- c(names(out[[5]]), "pvalue")
  return(tab)
}))
# use p.adjust with bonferroni correction
tenure$pvalue <- p.adjust(tenure$pvalue, method="fdr")
rownames(tenure) <- out.list
tenure <- tenure[order(tenure$pvalue),]
tenure$pvalue <- ifelse(tenure[, 3] < 0.001, "< 0.001", round(tenure[, 3], 3)) 
colnames(tenure) <- c("Non-Tubura", "Tubura Client", "p-value") 
print(kable(tenure))
Non-Tubura Tubura Client p-value
n_season_fert 2.109 4.762 < 0.001
n_season_compost 5.344 6.890 < 0.001
climbing_bean 0.161 0.295 < 0.001
slope 8.850 11.166 < 0.001
age 42.675 46.039 0.001
hillside 0.617 0.735 0.001
sorghum 0.172 0.086 0.001
n_seasons_leg_1 2.022 2.579 0.005
valley 0.320 0.226 0.008
cows 0.986 1.356 0.011
hhsize 5.205 5.599 0.012
female 0.459 0.551 0.018
alt 1509.106 1610.665 0.021
Total.N 0.155 0.149 0.059
own.cows 0.656 0.717 0.123
n_season_fallow 0.557 0.733 0.222
field.size 609.258 537.968 0.224
pH 5.485 5.420 0.224
own.pigs 0.388 0.336 0.224
hilltop 0.063 0.039 0.233
ExAl 0.585 0.645 0.235
m3.Mg 204.127 213.884 0.412
m3.Ca 815.244 767.659 0.421
sheep 0.205 0.262 0.474
own.chickens 0.328 0.360 0.474
sandy_soil 0.167 0.142 0.474
own.sheep 0.104 0.123 0.511
Total.C 2.090 2.065 0.574
red_soil 0.270 0.295 0.574
chickens 1.145 1.259 0.605
n_season_lime 0.227 0.259 0.616
maize 0.066 0.077 0.616
black_soil 0.538 0.519 0.662
n_seasons_leg_2 2.475 2.569 0.743
goats 1.060 1.103 0.751
own 0.940 0.945 0.788
bush_bean 0.158 0.151 0.788
pigs 0.533 0.548 0.844
own.goats 0.492 0.488 0.91

3.9 Tenured v. new balance interpretation

Demographic variables We are well balanced along demographic variables.

Agricultural practice variables Not surprisingly, Tubura farmers have more cumulative years of fertilizer use than current non-Tubura farmers. While that difference is signficant, it is realistically only a single season of fertilizer use different.

Interestingly, non-Tubura farmers reported using more lime than current Tubura farmers. This

Soil Variables Soil pH, calcium and magnesium levels are lower for tenured Tubura farmers. This is consistent with the hypothesis that increaesd fertilizer use leads to an increaese in soil acidity.

3.10 Analysis of agronomic practices contributing to soil health outcomes

Here’s where we’ll look at the contribution of fertilizer, lime and cultivation practices on soil health outcomes. This analysis will be come richer as we gain longitudinal measures. I caution that we cannot treat these relationships as causal. The direction of causality is not clearly delineated in the data or the study design. However, we can identify meaningful connections between practices and outcomes through this analysis to generate new hypotheses for field testing.

I’m going to start with behaviors by sections and then move to a more comprehensive model including multiple practices. All models will include controls for site to account for local variation and field officer behavior.

4 Analysis

4.1 Agronomic Behaviors

Check for multicollinearity before adding number of seasons of agronomic inputs on the same side of the regression.

suppressMessages(library(stargazer))
inputUse <- c("n_season_fert","n_season_compost", "n_season_lime", "n_season_fallow")
cor(d[,inputUse], use="complete.obs")
                 n_season_fert n_season_compost n_season_lime n_season_fallow
n_season_fert        1.0000000       0.35575631    0.17038250     -0.06419940
n_season_compost     0.3557563       1.00000000    0.03734895     -0.16878470
n_season_lime        0.1703825       0.03734895    1.00000000     -0.01112774
n_season_fallow     -0.0641994      -0.16878470   -0.01112774      1.00000000

Interpretation: The strongest correlation between the input use intensity variables is between seasons of fertilizer and compost use, ~0.35. While this is on the higher end it’s not necessarily cause for alarm.

inputUse <- paste(c("n_season_fert","n_season_compost", "n_season_lime", "n_season_fallow"), collapse= " + ")
list1 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", inputUse, "+ as.factor(cell)", sep="")), data=d)
  return(mod)
})
stargazer(list1, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Naive Agronomic Practice Models",
          covariate.labels = c("Seasons of Fertilizer", "Seasons of Compost", 
                               "Seasons of Lime", "Seasons of Fallow"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("m3.","", soilVars)),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste("output", "rw_baseline_agprac.htm", sep="/"))
2016A Rwanda Soil Health Baseline - Naive Agronomic Practice Models
Ca Mg pH Total.N Total.C ExAl
(1) (2) (3) (4) (5) (6)
Seasons of Fertilizer -3.601 -0.138 -0.002 -0.0001 -0.002 0.0003
(4.042) (0.776) (0.004) (0.0002) (0.003) (0.003)
Seasons of Compost 0.761 -0.007 0.004 0.00000 0.002 -0.005*
(3.270) (0.628) (0.003) (0.0002) (0.003) (0.003)
Seasons of Lime 42.291** 7.921** 0.011 0.002** 0.017 0.009
(17.647) (3.389) (0.016) (0.001) (0.014) (0.015)
Seasons of Fallow -32.572*** -3.291*** -0.038*** 0.001 0.007 0.025***
(6.433) (1.235) (0.006) (0.0004) (0.005) (0.005)
Constant 439.964 88.243 5.164*** 0.210*** 3.436*** 1.762***
(472.287) (90.710) (0.420) (0.026) (0.370) (0.403)
Observations 2,439 2,439 2,439 2,439 2,439 2,439
R2 0.507 0.497 0.574 0.503 0.439 0.561
Adjusted R2 0.462 0.451 0.535 0.458 0.388 0.521
Residual Std. Error (df = 2234) 472.106 90.675 0.420 0.026 0.369 0.403
F Statistic (df = 204; 2234) 11.266*** 10.817*** 14.741*** 11.083*** 8.561*** 13.994***
Note: p<0.1; p<0.05; p<0.01
Includes FE for cell

Interpretation The naive model suggests that when we include site level fixed effects, duration of agronomic practices don’t have a big effect on soil health outcomes. However, some of the practice intensity variables are not well distributed. Let’s take a look at a log transformation. I’m adding one to the variables as to not end up with lots of Inf values.

Log transformations in theory are appropriate for variables that are right skewed (vavlues clustered to the left of the distribution) and see diminishing returns to increasing values. The shape of the data suggests a log transformation but it’s debateable whether the relationship is diminishing.

agPrac <- c(names(d[grep('n_season_', names(d))]))
for(i in 1:length(agPrac)){
  print(
    ggplot(d, aes(x=d[,agPrac[i]])) + geom_density() +
      labs(x = paste(agPrac[i], " No transform", sep = ""))
        )
}

# since these are all skewed, consider a log transform 
for(i in 1:length(agPrac)){
  print(
    ggplot(d, aes(x=log10(d[,agPrac[i]]+1))) + geom_density() + 
      labs(x = paste(agPrac[i], " Log transform", sep = ""))
        )
}

# look at other transfomations
for(i in 1:length(agPrac)){
  print(
    ggplot(d, aes(x=d[,agPrac[i]]^(1/3))) + geom_density() +
      labs(x = paste(agPrac[i], " cubic root transform", sep = ""))
        )
}

# visualize the outcomes as well to see if a transformation is warranted
for(i in 1:length(soilVars)){
  print(
    ggplot(d, aes(x=d[,soilVars[i]])) + geom_density() +
      labs(x = soilVars[i], title = soilVars[i])
        )
}

d$logFert <- log(d$n_season_fert+1)
d$logCompost <- log(d$n_season_compost+1)
d$logLime <- log(d$n_season_lime+1)
d$logFallow <- log(d$n_season_fallow+1)

Or look at BoxCox graph to empirically determine the right transformation. Log is assuming a diminishing return to an increasing X. That’s probably not the case with fertilizer. We’d actually expect an increasing return as values get larger. We use boxcox to see what the data suggest. We interpret it as follows:

  • lambda = 2 -> square
  • lambda = 1 -> no transformation
  • lambda = 0.5 -> square root
  • lambda = 0 -> log
  • lamdba = -1 -> inverse
library(MASS)
for(i in 1:length(agPrac)){
boxcox(lm(pH ~ d[,agPrac[i]], data=d))
}

For pH at least it seems like a log transform is appropriate. We can run this for all other variables as well to see what we get as well.

Let’s look at the log results: See here and here for guidance on intepreting log transformed right hand side variables. See here for additional guidance on choosing a transformation.

How to interpret RHS log transform: For a linear multivariate OLS regression, we say “a one unit increase in X causes a (coefficient) change in Y.” For a linear-log regression where the X variable is log transformed, we say a L percent change in X leads to a (coefficient*L) change in Y.

Question: Do we want to run this analysis for only OAF farmers? If so, adjust the sample accordingly.

logVars <- paste(names(d[grep("log", names(d))]), collapse=" + ")
list2 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~",  logVars, "+ as.factor(cell)", sep="")), data=d)
  return(mod)
})
list2b <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~",  logVars, sep="")), data=d)
  return(mod)
})
suppressWarnings(
  stargazer(list2, list2b, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Log Agronomic Practice Models",
          covariate.labels = c("Seasons of Fertilizer (log)", "Seasons of Compost (log)", "Seasons of Lime (log)", "Seasons of Fallow (log)"),
          column.labels = c(rep(gsub("m3.","", soilVars),2)),
          dep.var.caption = "",
          dep.var.labels = c("",""),
          add.lines = list(c("Cell FE?", rep("Yes", 5), rep("No", 5))),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste("output", "rw_baseline_agprac_log.htm", sep="/"))
)
plm.log <- function(x, range){
  
  beta = round(summary(x)$coefficients[range,1],3)
  beta.pval = summary(x)$coefficients[range,4]
  beta.conv = ifelse(beta.pval < 0.01, "***", ifelse(
    beta.pval < 0.05, "**", ifelse(
      beta.pval < 0.1, "*", "")))
  #beta.pval = round(beta.pval, 3)
  outcome = paste(beta, beta.conv, sep = "")
  outcome = c(outcome, unique(round(summary(x)$coefficients[1,1],3)))
  res = data.frame(outcome, stringsAsFactors = F)
  
  return(res)  
}
rw.table15 <- do.call(cbind, lapply(list2, function(x){
  plm.log(x, 2:5)
}))
colnames(rw.table15) <- soilVars
rownames(rw.table15) <- c(names(d)[grep("log", names(d))], "constant")

4.2 Table 15 outcomes

rw.table15 <- rw.table15[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(rw.table15, file=paste("output", "rwtable15_reg.csv", sep = "/"),
          row.names = T)
# a 10 percent increase in x leads to B1*.1 change in Y
logTrans <- do.call(cbind, lapply(list2, function(x){
  coeff = x$coefficients[2:5]
  tenPercent = round(coeff*.1, 5)
  names(tenPercent) <- paste("10% increase in ", gsub("log","", names(tenPercent)), " leads to:", sep="")
  return(tenPercent)
}))
colnames(logTrans) <- soilVars
print(kable(logTrans))
m3.Ca m3.Mg pH Total.N Total.C ExAl
10% increase in Fert leads to: -1.78758 -0.11370 -0.00150 -0.00004 -0.00089 0.00048
10% increase in Compost leads to: 0.22044 -0.02094 0.00129 -0.00002 0.00061 -0.00176
10% increase in Lime leads to: 14.37415 2.65234 0.00599 0.00074 0.00658 0.00030
10% increase in Fallow leads to: -11.54532 -1.13393 -0.01315 0.00013 0.00177 0.00873

Interpretation: When we transform the variables to log, the data starts to tell a more coherent story, at least directionally. If we remove the district FE, the coefficients gain significance.

  • Additional seasons of fertilizer use relates to a decrease in soil N and C.
  • Seasons of compost and lime have the opposite, positive effect on soil N.
  • Unsurprisingly, soil health metrics are poorer for soils on which more fertilizer has been used and lime has not been used.

4.2.1 Tenure with One Acre Fund

Let’s look first at a naive model of One Acre Fund tenure on soil health. Remember: these data are not longitudinal! These data are not longitudinal and reflect farmer selection into One Acre Fund. While these models will try to be both robust and parsimonious, we will inevitabily suffer omitted variable bias due to a lack of an instrument.

list3 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~",  "total.seasons + as.factor(cell)", sep="")), data=d)
  return(mod)
})
stargazer(list3, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Naive Tenure Models",
          covariate.labels = c("OAF Tenure"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("m3.","", soilVars)),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste("output", "rw_baseline_tenure.htm", sep="/"))
2016A Rwanda Soil Health Baseline - Naive Tenure Models
Ca Mg pH Total.N Total.C ExAl
(1) (2) (3) (4) (5) (6)
OAF Tenure -0.129 0.745 0.001 -0.0002 -0.004 -0.002
(3.471) (0.663) (0.003) (0.0002) (0.003) (0.003)
Constant 443.267 86.727 5.178*** 0.210*** 3.451*** 1.746***
(475.253) (90.857) (0.424) (0.026) (0.369) (0.406)
Observations 2,439 2,439 2,439 2,439 2,439 2,439
R2 0.500 0.494 0.564 0.501 0.438 0.556
Adjusted R2 0.455 0.449 0.525 0.457 0.388 0.516
Residual Std. Error (df = 2237) 475.202 90.847 0.424 0.026 0.369 0.405
F Statistic (df = 201; 2237) 11.127*** 10.880*** 14.408*** 11.196*** 8.684*** 13.913***
Note: p<0.1; p<0.05; p<0.01
Includes FE for cell

Interpretation: The naive One Acre Fund tenure model suggest that across the board that additional years of 1AF practices have a negative effect on soil health parameters. Let’s combine 1AF tenure with the agronomic practices model above to build a more robust model:

list4 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~",  logVars, "+ total.seasons + as.factor(cell)", sep="")), data=d)
  return(mod)
})
stargazer(list4, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Ag Practice and Tenure",
          covariate.labels = c("Seasons of Fertilizer (log)", "Seasons of Compost (log)", "Seasons of Lime (log)", "Seasons of Fallow (log)", "OAF Tenure"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("m3.","", soilVars)),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste("output", "rw_baseline_ag_tenure.htm", sep="/"))
2016A Rwanda Soil Health Baseline - Ag Practice and Tenure
Ca Mg pH Total.N Total.C ExAl
(1) (2) (3) (4) (5) (6)
Seasons of Fertilizer (log) -22.634 -3.440 -0.024 0.0003 0.002 0.014
(16.929) (3.258) (0.015) (0.001) (0.013) (0.014)
Seasons of Compost (log) 2.280 -0.173 0.013 -0.0003 0.006 -0.018
(14.833) (2.854) (0.013) (0.001) (0.012) (0.013)
Seasons of Lime (log) 143.969*** 26.634*** 0.060* 0.007*** 0.065** 0.003
(39.808) (7.660) (0.035) (0.002) (0.031) (0.034)
Seasons of Fallow (log) -115.872*** -11.542*** -0.132*** 0.001 0.019 0.088***
(18.857) (3.629) (0.017) (0.001) (0.015) (0.016)
OAF Tenure 2.125 1.029 0.004 -0.0003 -0.005 -0.004
(4.209) (0.810) (0.004) (0.0002) (0.003) (0.004)
Constant 435.090 86.437 5.151*** 0.211*** 3.444*** 1.779***
(470.889) (90.611) (0.418) (0.026) (0.370) (0.403)
Observations 2,439 2,439 2,439 2,439 2,439 2,439
R2 0.511 0.499 0.578 0.505 0.440 0.562
Adjusted R2 0.466 0.453 0.539 0.459 0.388 0.522
Residual Std. Error (df = 2233) 470.204 90.479 0.418 0.026 0.369 0.403
F Statistic (df = 205; 2233) 11.396*** 10.863*** 14.891*** 11.092*** 8.556*** 14.004***
Note: p<0.1; p<0.05; p<0.01
Includes FE for cell

Interpretation: Including agronomic practices and 1AF tenure in the same model dampens the magnitude, but not the significance, of 1AF tenure on soil health outcomes.

4.3 Agronomic practices - 15B cultivation practices

Thus far we have looked at aggregated historical plot level practices and their effect on soil health. We also asked farmers about their cultivation practices on their plot in the previous season, 15B. We have more precise information for fertilizer, compost and liming practices for the 15B season.

# scale all the field application variables to ares
d$ares <- d$field.size/100
d$fert1.are <- d$field_kg_fert1_15b/d$ares
d$fert2.are <- d$field_kg_fert2_15b/d$ares
d$fert.total.are <- apply(d[,c("fert1.are", "fert2.are")], 1, function(x){
  sum(x, na.rm=T)
})
d$fert.total.are <- ifelse(is.na(d$fert1.are) & is.na(d$fert2.are), NA, d$fert.total.are)
d$compost.are <- d$field_kg_compost_15b/d$ares
d$lime.are <- d$kg_lime_15b/d$ares
intensityVars <- c("fert1.are", "fert2.are",
                   "compost.are", "lime.are")
cor(d[,intensityVars], use="complete.obs")
            fert1.are fert2.are compost.are  lime.are
fert1.are   1.0000000 0.9889407   0.5216134 0.8679373
fert2.are   0.9889407 1.0000000   0.5328874 0.8539114
compost.are 0.5216134 0.5328874   1.0000000 0.6236616
lime.are    0.8679373 0.8539114   0.6236616 1.0000000
#table(d$field_15b_fert_t, useNA = 'ifany')
d$field_15b_fert_t <- tolower(d$field_15b_fert_t)
names(d)[names(d)=="v69"] <- "field_15b_fert_t2"
#table(d$field_15b_fert_t2)
d$field_15b_fert_t2 <- tolower(d$field_15b_fert_t2)
d$dap1 <- ifelse(d$field_15b_fert_t=="dap", d$field_kg_fert1_15b, NA)
d$dap1.are <- d$dap1/d$ares
d$npk1 <- ifelse(grepl("npk", d$field_15b_fert_t), d$field_kg_fert1_15b, NA)
d$npk1.are <- d$npk1/d$ares
d$urea1 <- ifelse(d$field_15b_fert_t=="urea", d$field_kg_fert1_15b, NA)
d$urea1.are <- d$urea1/d$ares
d$urea2 <- ifelse(d$field_15b_fert_t2=="urea", d$field_kg_fert2_15b, NA)
d$urea2.are <- d$urea2/d$ares
d$total.urea.are <- apply(d[,c("urea1.are", "urea2.are")], 1, function(x){
  sum(x, na.rm=T)
})
d$total.urea.are <- ifelse(is.na(d$urea1.are) & is.na(d$urea2.are), NA, d$total.urea.are)

4.3.1 Graph the application/are variables

for(i in 1:length(intensityVars)){
  print(
  ggplot(d, aes(x=d[,intensityVars[i]])) + geom_density() +
      labs(x = intensityVars[i], title = intensityVars[i])
  )
}

Conclusion - Take 1: The application rate per are variables are weird. I think it’s because of the field dimensions. I’m going to go back to the field dimensions and check this.

Let’s look at the dimensions of the fields that have large application rates

d[d$fert1.are>10 & !is.na(d$fert1.are), c("field_dim1", "field_dim2", "field.size", "fert1.are")]
d[d$fert2.are>10 & !is.na(d$fert2.are), c("field_dim1", "field_dim2", "field.size", "fert2.are")]
d[d$compost.are>500 & !is.na(d$compost.are), c("field_dim1", "field_dim2", "field.size", "compost.are")]
# there's a field that is 1 meter wide? Surely not.
d[abs(d$lime.are)>40 & !is.na(d$lime.are), c("field_dim1", "field_dim2", "field.size", "lime.are")]
# how is there a negative quantity of lime?

4.4 Tenure Scatter plots

Generate tenure vs. pH and tenure vs. fertilizer seasons scatter plots:

tenure.ph <- ggplot(d, aes(x=jitter(total.seasons), y=pH)) + geom_point() + 
  geom_smooth(method="loess") +
  labs(x = "OAF Tenure", y = "Soil pH", title = "OAF Tenure vs. soil pH in Rwanda")
tenure.ph
pdf(paste("output", "rw tenure vs ph.pdf", sep = "/"), width=11, height=8.5)
print(tenure.ph)
dev.off()
quartz_off_screen 
                2 

tenure.fertilizer <- ggplot(d, aes(x= jitter(total.seasons), y=jitter(n_season_fert))) + geom_point() +
  geom_smooth(method='loess') + 
  labs(x = "OAF Tenure", y = "Years of Fertilizer in past 5 years", title = "OAF Tenure vs. soil pH in Rwanda")
tenure.fertilizer
pdf(paste("output", "rw tenure vs fertilizer.pdf", sep = "/"), width=11, height=8.5)
print(tenure.fertilizer)
dev.off()
quartz_off_screen 
                2 

4.5 Table 16 - Kgs of soil ammendments - fertilizer (kg)

# this should be kg of fertilizer used in this field. Compost is off the charts. Convert this to compost per sq meter
plm.t16 <- function(x, range){
  
  beta = round(summary(x)$coefficients[range,1],3)
  beta.pval = round(summary(x)$coefficients[range,4],3)
  beta.conv = ifelse(beta.pval < 0.01, "***", ifelse(
    beta.pval < 0.05, "**", ifelse(
      beta.pval < 0.1, "*", "")))
  #beta.pval = round(beta.pval, 3)
  outcome = paste(beta, " (", beta.pval, ")", sep = "")
  outcome = c(outcome, unique(round(summary(x)$coefficients[1,1],3)))
  res = data.frame(outcome, stringsAsFactors = F)
  
  return(res)  
}
previousSeasonfert <- paste("fert1.are", "as.factor(cell)", sep=" + ")
list5 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonfert, sep="")), data=d)
  return(mod)
})
table16 <- do.call(cbind, lapply(list5, function(x){
  plm.t16(x, 2)
}))
colnames(table16) <- soilVars
rownames(table16) <- c("Fertilizer (kg/are)", "constant")
table16 <- table16[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16, file=paste("output", "rwtable16_fert.csv", sep = "/"),
          row.names = T)

4.6 Table 16 - all fertilizer used

previousSeasonfertAll <- paste("fert.total.are", "as.factor(cell)", sep=" + ")
list5a <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonfertAll, sep="")), data=d)
  return(mod)
})
table16a <- do.call(cbind, lapply(list5a, function(x){
  plm.t16(x, 2)
}))
colnames(table16a) <- soilVars
rownames(table16a) <- c("Fertilizer (all) (kg/are)", "constant")
table16a <- table16a[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16a, file=paste("output", "rwtable16_fert_all.csv", sep = "/"),
          row.names = T)

4.7 Table 16 - Kgs of soil ammendments - compost (kg)

# these objects have the same name as the fertilizer objects for simplicity. Run
# from the top to avoid overwriting issues.
previousSeasoncompost <- paste("compost.are", "as.factor(cell)", sep=" + ")
temp <- subset(d, d$compost.are>0)
list5b <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("temp[,x] ~", previousSeasoncompost, sep="")), data=temp)
  return(mod)
})
table16b <- do.call(cbind, lapply(list5b, function(x){
  plm.t16(x, 2)
}))
colnames(table16b) <- soilVars
rownames(table16b) <- c("Compost (kg/are)", "constant")
table16b <- table16b[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16b, file=paste("output", "rwtable16_compost.csv", sep = "/"),
          row.names = T)

The compost regression results are suprising. Look at Carbon and compost scatter plot. If we include 0 compost, we see no relationship. If we exclude 0 compost, we

soilC.compost <- ggplot(temp, aes(x = compost.are, y=Total.C)) + geom_point() + 
  scale_x_continuous(limits=c(0,50)) + # remove larger values from the graph.
  geom_smooth(method='lm') +
  labs(title = "Compost and Soil Carbon in Rwanda", x = "Compost (kg/acre)", y = "Total Carbon")
soilC.compost
pdf(paste("output", "compost vs soil c.pdf", sep = "/"), width=11, height=8.5)
print(soilC.compost)
dev.off()
quartz_off_screen 
                2 

There are too many zero or near zero values. We can conclude that perhaps we’re not asking this question well. Even when we remove non-zero values, there’s little discernable relationship.

4.8 Table 16 - Kgs of soil ammendments - lime (kg)

# these objects have the same name as the fertilizer objects for simplicity. Run
# from the top to avoid overwriting issues.
previousSeasonlime <- paste("lime.are", "as.factor(cell)", sep=" + ")
list5c <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonlime, sep="")), data=d)
  return(mod)
})
table16c <- do.call(cbind, lapply(list5c, function(x){
  plm.t16(x, 2)
}))
colnames(table16c) <- soilVars
rownames(table16c) <- c("Lime (kg/are)", "constant")
table16c <- table16c[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16c, file=paste("output", "rwtable16_lime.csv", sep = "/"),
          row.names = T)

4.9 Table 16 - types of fertilizer - DAP (kg)

previousSeasonDAP <- paste("dap1.are", "as.factor(cell)", sep=" + ")
list5d <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonDAP, sep="")), data=d)
  return(mod)
})
table16d <- do.call(cbind, lapply(list5d, function(x){
  plm.t16(x, 2)
}))
colnames(table16d) <- soilVars
rownames(table16d) <- c("DAP (kg/are)", "constant")
table16d <- table16d[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16d, file=paste("output", "rwtable16_dap.csv", sep = "/"),
          row.names = T)

4.10 Table 16 - types of fertilizer - NPK (kg)

previousSeasonNPK <- paste("npk1.are", "as.factor(cell)", sep=" + ")
list5e <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonNPK, sep="")), data=d)
  return(mod)
})
table16e <- do.call(cbind, lapply(list5e, function(x){
  plm.t16(x, 2)
}))
colnames(table16e) <- soilVars
rownames(table16e) <- c("NPK (kg/are)", "constant")
table16e <- table16e[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16e, file=paste("output", "rwtable16_npk.csv", sep = "/"),
          row.names = T)

4.11 Table 16 - types of fertilizer - Urea (kg)

previousSeasonUrea <- paste("total.urea.are", "as.factor(cell)", sep=" + ")
list5f <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", previousSeasonUrea, sep="")), data=d)
  return(mod)
})
table16f <- do.call(cbind, lapply(list5f, function(x){
  plm.t16(x, 2)
}))
colnames(table16f) <- soilVars
rownames(table16f) <- c("Urea (kg/are)", "constant")
table16f <- table16f[,c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")]
write.csv(table16f, file=paste("output", "rwtable16_urea.csv", sep = "/"),
          row.names = T)
stargazer(list5, type="html", 
          title = "2016A Rwanda Soil Health Baseline - 15B practices",
          covariate.labels = c("Fertilizer Rate (log)"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("m3.","", soilVars)),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste("output", "rw_baseline_15b_ag.htm", sep="/"))

4.12 Farmer fertility perception

Let’s look at farmer perceived fertility as a predictor of soil health. We’ll set ‘same fertility’ as the reference category.

d$fertility_qual <- relevel(as.factor(d$general_field_infocompare_fertil), ref="same")
list6 <- lapply(soilVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", "+ fertility_qual + as.factor(cell)", sep="")), data=d)
  return(mod)
})
stargazer(list6, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility",
          covariate.labels = c("Farmer Opinion - Less Fertile",
                               "Farmer Opinion - More Fertile"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("m3.","", soilVars)),
          notes = "Reference category is same fertility as other fields. Includes FE for cell",
          notes.align = "l",
          omit=c("cell"), out=paste("output", "rw_baseline_fertility_qual.htm", sep="/"))
2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility
Ca Mg pH Total.N Total.C ExAl
(1) (2) (3) (4) (5) (6)
Farmer Opinion - Less Fertile -128.212*** -17.537*** -0.134*** 0.002 0.013 0.116***
(26.465) (5.077) (0.024) (0.001) (0.021) (0.023)
Farmer Opinion - More Fertile 22.239 1.717 0.014 -0.001 -0.009 -0.017
(29.278) (5.617) (0.026) (0.002) (0.023) (0.025)
Constant 571.222 105.753 5.314*** 0.208*** 3.430*** 1.626***
(473.062) (90.758) (0.421) (0.026) (0.370) (0.403)
Observations 2,439 2,439 2,439 2,439 2,439 2,439
R2 0.506 0.497 0.571 0.502 0.438 0.562
Adjusted R2 0.462 0.452 0.533 0.457 0.387 0.522
Residual Std. Error (df = 2236) 472.321 90.616 0.421 0.026 0.370 0.403
F Statistic (df = 202; 2236) 11.348*** 10.943*** 14.756*** 11.160*** 8.628*** 14.179***
Note: p<0.1; p<0.05; p<0.01
Reference category is same fertility as other fields. Includes FE for cell

Interpretation: Farmers understand their fields well. Their categorization of which field are more and less fertile corresponds to our quantified measures of soil health. The only features farmers don’t seem to get correct are nitrogen and carbon. The nitrogen and carbon levels are indistinguishable in ‘low fertility’ fields relative to the fields deemed to be the ‘same fertility.’ Reminder: We need to remember that farmers are only evaluting one of their fields thus we are not able to account for the quality of the farmer in assessing his/her fields.

4.12.1 Deeper dive into farmer perception of fertility

  • Look at wet chem values for all soil features by farmer perception
  • Run PCA on all soil attributes to see which principle components predict soil fertility
  • Consider adding additional control variables into model (like Ca as control for pH perception model)
# merge wetChem in with d
names(wetChem)[2:21] <- paste("wet.", names(wetChem)[2:21], sep = "")
d <- left_join(d, wetChem, by="SSN")
joining factor and character vector, coercing into character vector

4.12.2 For Eric, CEC summary for new Ca and Mg thresholds

ggplot(d, aes(x=wet.C.E.C)) + geom_density() + 
  geom_vline(xintercept = median(d$wet.C.E.C, na.rm=T), color="red") + 
  geom_vline(xintercept = mean(d$wet.C.E.C, na.rm=T), color="blue")

summary(d$wet.C.E.C)[3:4]
Median   Mean 
 8.620  9.684 
wetVars <- names(d)[grep("wet.", names(d))]
for(i in 1:length(wetVars)){
  print(
  ggplot(data=d, aes(x=as.factor(client), y=d[,wetVars[i]])) + 
    geom_boxplot() +
    labs(x="Tubura Farmer", y=wetVars[i], title = paste("RW baseline wet chem - ", wetVars[i], sep = ""))
  )
}

list7 <- lapply(wetVars, function(x){
  mod <- lm(as.formula(paste("d[,x] ~", "+ fertility_qual + as.factor(cell)", sep="")), data=d)
  return(mod)
})
suppressWarnings(
stargazer(list7, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility (wet chem)",
          covariate.labels = c("Farmer Opinion - Less Fertile",
                               "Farmer Opinion - More Fertile"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("wet.","", wetVars)),
          notes = "Reference category is same fertility as other fields. Includes FE for cell",
          notes.align = "l",
          omit=c("cell"), out=paste("output", "rw_baseline_fertility_qual_wet.htm", sep="/"))
)
2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility (wet chem)
pH EC..S. P K Ca Mg S Na Fe Mn B Cu Zn C.E.C TN C.N Exch..Acidity Acid.Saturation Exch.Al OC
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20)
Farmer Opinion - Less Fertile -0.228 -5.566 -8.792 -55.794* -74.347 -35.860 2.483 -1.881 19.090 -33.558 -0.075 -0.424 -0.334 -0.499 0.001 -0.201 0.385** 10.942*** 0.302** 0.063
(0.149) (11.230) (19.805) (28.523) (150.909) (36.429) (2.272) (2.951) (17.447) (21.295) (0.225) (0.339) (0.772) (1.119) (0.008) (0.632) (0.163) (3.777) (0.126) (0.137)
Farmer Opinion - More Fertile -0.112 -11.733 -11.765 -80.863** -174.963 -71.564 2.687 -1.391 -13.159 -11.052 -0.061 -0.250 -0.880 -1.523 -0.023** 0.318 0.157 4.668 0.119 -0.277
(0.186) (14.042) (25.228) (36.333) (192.230) (46.404) (2.895) (3.759) (22.224) (27.126) (0.286) (0.432) (0.983) (1.425) (0.010) (0.806) (0.208) (4.811) (0.161) (0.174)
Constant 6.225*** 127.000*** 8.910 213.000*** 1,745.000*** 435.000*** 15.700** 24.300*** 61.450 194.000*** 0.415 2.670*** 4.415** 15.650*** 0.195*** 11.750*** 0.140 1.100 0.023 2.335***
(0.409) (30.862) (55.804) (80.369) (425.217) (102.647) (6.403) (8.315) (49.160) (60.004) (0.633) (0.957) (2.175) (3.152) (0.022) (1.782) (0.459) (10.642) (0.356) (0.385)
Observations 234 234 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241
R2 0.735 0.594 0.705 0.661 0.750 0.761 0.727 0.706 0.762 0.695 0.525 0.695 0.540 0.751 0.792 0.670 0.769 0.769 0.793 0.733
Adjusted R2 0.407 0.091 0.350 0.254 0.450 0.473 0.398 0.353 0.476 0.330 -0.046 0.328 -0.013 0.451 0.541 0.274 0.491 0.492 0.544 0.412
Residual Std. Error 0.579 (df = 104) 43.645 (df = 104) 78.919 (df = 109) 113.659 (df = 109) 601.348 (df = 109) 145.165 (df = 109) 9.055 (df = 109) 11.760 (df = 109) 69.523 (df = 109) 84.858 (df = 109) 0.895 (df = 109) 1.353 (df = 109) 3.077 (df = 109) 4.458 (df = 109) 0.032 (df = 109) 2.520 (df = 109) 0.649 (df = 109) 15.049 (df = 109) 0.503 (df = 109) 0.544 (df = 109)
F Statistic 2.238*** (df = 129; 104) 1.180 (df = 129; 104) 1.985*** (df = 131; 109) 1.625*** (df = 131; 109) 2.498*** (df = 131; 109) 2.642*** (df = 131; 109) 2.214*** (df = 131; 109) 2.001*** (df = 131; 109) 2.661*** (df = 131; 109) 1.900*** (df = 131; 109) 0.920 (df = 131; 109) 1.894*** (df = 131; 109) 0.976 (df = 131; 109) 2.503*** (df = 131; 109) 3.160*** (df = 131; 109) 1.691*** (df = 131; 109) 2.765*** (df = 131; 109) 2.772*** (df = 131; 109) 3.189*** (df = 131; 109) 2.283*** (df = 131; 109)
Note: p<0.1; p<0.05; p<0.01
Reference category is same fertility as other fields. Includes FE for cell

Interpretation: Our sample size decreases considerably when we look only at wet chemistry results. Thus, we do not see the same signficant relationships we saw between farmer perceived fertility and soil characteristics we saw when looking at the predicted values.

4.13 Principal Component Analysis (predicted values)

pca1 <- prcomp(d[,soilVars], scale=TRUE, center=TRUE)
print(pca1)
Standard deviations:
[1] 1.7694781 1.4197211 0.5985909 0.4836832 0.4092214 0.3059685

Rotation:
                 PC1          PC2           PC3        PC4         PC5         PC6
m3.Ca    0.497746227 -0.233189315  0.0200217427 -0.5908066 -0.05845757  0.58736772
m3.Mg    0.477406109 -0.130704228 -0.8045486852  0.2566894 -0.09624119 -0.18041456
pH       0.531809652  0.007760003  0.3663922355 -0.2518775  0.19398670 -0.69411952
Total.N  0.005638237 -0.663812926  0.2859668476  0.2266671 -0.64277520 -0.11404215
Total.C -0.100238773 -0.658444962 -0.0007406172  0.1625259  0.72558685  0.05925313
ExAl    -0.481072753 -0.232984039 -0.3691607865 -0.6662006 -0.10026596 -0.35232273
plot(pca1)

pca1$rotation
                 PC1          PC2           PC3        PC4         PC5         PC6
m3.Ca    0.497746227 -0.233189315  0.0200217427 -0.5908066 -0.05845757  0.58736772
m3.Mg    0.477406109 -0.130704228 -0.8045486852  0.2566894 -0.09624119 -0.18041456
pH       0.531809652  0.007760003  0.3663922355 -0.2518775  0.19398670 -0.69411952
Total.N  0.005638237 -0.663812926  0.2859668476  0.2266671 -0.64277520 -0.11404215
Total.C -0.100238773 -0.658444962 -0.0007406172  0.1625259  0.72558685  0.05925313
ExAl    -0.481072753 -0.232984039 -0.3691607865 -0.6662006 -0.10026596 -0.35232273

The first principal component is composed primarily of soil pH related variables, Ca, Mg, and pH. The second capture N and C. These groupings (pH grouping and C and N) are not all that surprising given that our predicted soil variable set is fairly limited.

If the variables have the same sign that indicates that they are positively correlated in the principal component. In the first principal component, we see Ca, Mg and pH loading in the same direction. In the second principal component, we see N and C moving in the same direction. Ca and Mg are also associated in the same direction but to a lesser extent.

ggplot(as.data.frame(pca1$x),aes(x=PC1,y=PC2, color=d$fertility_qual)) + geom_point() + 
  labs(title = "PCA of soil attributes and farmer perceived soil fertility",
       x = "First PCA", y= "Second PCA", color="Field Quality")

When we layer farmer perceived soil fertility on top of the principal component scatter plot, no clear pattern emergres. Fields with the same fertilitiy, less and more fertility are indistinguishable by the principal components. Thus, there is not a clear profile for farmer identified healthier soil or weaker soil based on principal components.

4.13.1 Geographic Descriptors - PCA for soil profiles

This section will build on the principal component work above and look at improving understanding of local context to inform local adaptation. Here we will also construct soil profiles to simplify the scaling of promising products and practices to targeted locations. We don’t have a full suite of predictors so we can’t look at a comprehensive soil profile.

ggplot(as.data.frame(pca1$x),aes(x=PC1,y=PC2, color=d$district)) + geom_point() + 
  labs(title = "PCA of soil attributes and district",
       x = "First PCA", y= "Second PCA", color="District")

When we color the figure by district, we start to see a pattern emerge. However, there are too many points to clearly detect where all districts fall. Let’s instead look at the data by AEZ.

ggplot(as.data.frame(pca1$x),aes(x=PC1,y=PC2, color=d$aez)) + geom_point() + 
  labs(title = "PCA of soil attributes and AEZ",
       x = "First PCA - Ca/Mg/pH", y= "Second PCA - N and C", color="AEZ")

Coloring the points by AEZ, we see a much clearer trend. The east is to the right of our graphic, then the central plateau followed by lake Kive and then Congo Nile in green on the left. What does this mean in terms of actual soil features? Let’s look at the figure but with the variable associations layered on top. See here for documentation

library(pca3d)
pca2d( pca1, biplot= TRUE, shape= 19, col= "black"  )

It appears that the right side of the graph, the eastern AEZ, is associated with pH, Mg and Ca. This indicates that as the first principal component increases, so does the level of pH, Ca and Mg. Conversely, total N and C increase as the second principal component decreases. In terms of the AEZ figure above, this suggest that the Congo Nile AEZ has higher levels of N and C. Let’s test these hypotheses with a simple summary table:

print(kable(aggregate(d[,soilVars], by=list(d$aez), function(x){
  round(mean(x, na.rm=T),5)
})))
Group.1 m3.Ca m3.Mg pH Total.N Total.C ExAl
Central Plateau 793.5114 207.0087 5.53865 0.13754 1.87063 0.43171
Congo Nile 396.7151 125.7663 4.96555 0.16072 2.30810 1.14672
East 1341.5413 274.0356 5.93730 0.17050 2.12779 0.23845
Lake Kivu 742.6964 229.2307 5.47636 0.13980 2.01218 0.56104

Confirmed! This likely also suggests that Congo Nile is at a higher altitude than the surrounding areas and that eastern Rwanda has relatively less weathered soils compared to western.

Consequence for trial placement: The Rwanda program is already blocking trials by AEZ but these data confirm that AEZ reflects meaningfu soil variation and thus captures key growing conditions for Rwandan farmers. Blocking trials by AEZ in Rwanda will enable us to evaluate trial hypotheses in more neutral pH ranges and higher N and C conditions.

4.13.2 Principal Component Analysis (wet chemistry values)

wetVal <- d[complete.cases(d[,wetVars]),]
pca2 <- prcomp(wetVal[,c("wet.C.E.C", "wet.pH", "wet.Mg", "wet.Ca")], center=TRUE, scale=TRUE)
pca2 <- prcomp(wetVal[, wetVars], center=TRUE, scale=TRUE)
#plot(pca2)
ggplot(as.data.frame(pca2$x),aes(x=PC1,y=PC2, color=as.factor(wetVal$aez))) + geom_point() +
  labs(title = "Wet Chem PCA with AEZ", x = "First PC", y="Second PC",
       color="AEZ")

#pca2$rotation
pca2d(pca2, biplot= TRUE, shape= 19, col= "black")

# put pca2$x PC1 in the main data to run the fertility perception model.
#mod8 <- lm(
suppressWarnings(
stargazer(list7, type="html", 
          title = "2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility (wet chem)",
          covariate.labels = c("Farmer Opinion - Less Fertile",
                               "Farmer Opinion - More Fertile"),
          dep.var.caption = "",
          dep.var.labels = "",
          column.labels = c(gsub("wet.","", wetVars)),
          notes = "Reference category is same fertility as other fields. Includes FE for cell",
          notes.align = "l",
          omit=c("cell"), out=paste("output", "rw_baseline_fertility_qual_wet.htm", sep="/"))
)
2016A Rwanda Soil Health Baseline - Farmer Perceived Fertility (wet chem)
pH EC..S. P K Ca Mg S Na Fe Mn B Cu Zn C.E.C TN C.N Exch..Acidity Acid.Saturation Exch.Al OC
(1) (2) (3) (4) (5) (6) (7) (8) (9) (10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20)
Farmer Opinion - Less Fertile -0.228 -5.566 -8.792 -55.794* -74.347 -35.860 2.483 -1.881 19.090 -33.558 -0.075 -0.424 -0.334 -0.499 0.001 -0.201 0.385** 10.942*** 0.302** 0.063
(0.149) (11.230) (19.805) (28.523) (150.909) (36.429) (2.272) (2.951) (17.447) (21.295) (0.225) (0.339) (0.772) (1.119) (0.008) (0.632) (0.163) (3.777) (0.126) (0.137)
Farmer Opinion - More Fertile -0.112 -11.733 -11.765 -80.863** -174.963 -71.564 2.687 -1.391 -13.159 -11.052 -0.061 -0.250 -0.880 -1.523 -0.023** 0.318 0.157 4.668 0.119 -0.277
(0.186) (14.042) (25.228) (36.333) (192.230) (46.404) (2.895) (3.759) (22.224) (27.126) (0.286) (0.432) (0.983) (1.425) (0.010) (0.806) (0.208) (4.811) (0.161) (0.174)
Constant 6.225*** 127.000*** 8.910 213.000*** 1,745.000*** 435.000*** 15.700** 24.300*** 61.450 194.000*** 0.415 2.670*** 4.415** 15.650*** 0.195*** 11.750*** 0.140 1.100 0.023 2.335***
(0.409) (30.862) (55.804) (80.369) (425.217) (102.647) (6.403) (8.315) (49.160) (60.004) (0.633) (0.957) (2.175) (3.152) (0.022) (1.782) (0.459) (10.642) (0.356) (0.385)
Observations 234 234 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241 241
R2 0.735 0.594 0.705 0.661 0.750 0.761 0.727 0.706 0.762 0.695 0.525 0.695 0.540 0.751 0.792 0.670 0.769 0.769 0.793 0.733
Adjusted R2 0.407 0.091 0.350 0.254 0.450 0.473 0.398 0.353 0.476 0.330 -0.046 0.328 -0.013 0.451 0.541 0.274 0.491 0.492 0.544 0.412
Residual Std. Error 0.579 (df = 104) 43.645 (df = 104) 78.919 (df = 109) 113.659 (df = 109) 601.348 (df = 109) 145.165 (df = 109) 9.055 (df = 109) 11.760 (df = 109) 69.523 (df = 109) 84.858 (df = 109) 0.895 (df = 109) 1.353 (df = 109) 3.077 (df = 109) 4.458 (df = 109) 0.032 (df = 109) 2.520 (df = 109) 0.649 (df = 109) 15.049 (df = 109) 0.503 (df = 109) 0.544 (df = 109)
F Statistic 2.238*** (df = 129; 104) 1.180 (df = 129; 104) 1.985*** (df = 131; 109) 1.625*** (df = 131; 109) 2.498*** (df = 131; 109) 2.642*** (df = 131; 109) 2.214*** (df = 131; 109) 2.001*** (df = 131; 109) 2.661*** (df = 131; 109) 1.900*** (df = 131; 109) 0.920 (df = 131; 109) 1.894*** (df = 131; 109) 0.976 (df = 131; 109) 2.503*** (df = 131; 109) 3.160*** (df = 131; 109) 1.691*** (df = 131; 109) 2.765*** (df = 131; 109) 2.772*** (df = 131; 109) 3.189*** (df = 131; 109) 2.283*** (df = 131; 109)
Note: p<0.1; p<0.05; p<0.01
Reference category is same fertility as other fields. Includes FE for cell

4.14 District and cell level summaries of soil and managment practices

dist.sum <- aggregate(d[,out.list], by=list(d$district), function(x){
  return(c(
    paste(
      round(mean(x, na.rm=T),3), " (", round(median(x, na.rm=T),3), ")", 
      " (", round(sd(x, na.rm=T),2), ")", sep = ""
    )
    ))
})
write.csv(dist.sum, file=paste("output", "district covariate summary.csv", sep = "/"))
cell.sum <- aggregate(d[,out.list], by=list(d$cell), function(x){
  return(c(
    paste(
      round(mean(x, na.rm=T),3), " (", round(median(x, na.rm=T),3), ")", 
      " (", round(sd(x, na.rm=T),2), ")", sep = ""
    )
    ))
})
write.csv(cell.sum, file=paste("output", "cell covariate summary.csv", sep = "/"))

4.15 Female farmers farming poorer soils?

remove <- "female"
genderBalance <- out.list[!out.list %in% remove]
equal <- do.call(rbind, lapply(genderBalance, function(x){
    
    out <- t.test(d[,x] ~ d[,"female"], data=d)
    tab <- data.frame(out[[5]][[2]],out[[5]][[1]], out[3])
    tab[,1:2] <- round(tab[,1:2],3)
    names(tab) <- c(names(out[[5]]), "pvalue")
    #tab[,3] <- p.adjust(tab[,3], method="holm")
    #tab[,3] <- ifelse(tab[,3] < 0.001, "< 0.001", round(tab[,3],3))
    #print(tab)
    return(tab)
  
}))
rownames(equal) <- NULL
# order variables 
equal$pvalue <- p.adjust(equal$pvalue, method="fdr")
rownames(equal) <- genderBalance
equal <- equal[order(equal$pvalue),]
equal$pvalue <- ifelse(equal$pvalue < 0.001, "< 0.001", round(equal$pvalue,3))
colnames(equal) <- c("Male Farmers","Female Farmers", "p-value")    
write.csv(equal, file=paste("output", "rw female farmer status.csv", sep = "/"), row.names=T)

4.16 Export the data for Eric

write.csv(d, file=paste("output", "rwanda_shs_merged_clean.csv",sep = "/"), row.names = F)

5 Propensity Score Matching

We need to do a more rigorous job of accounting for differences between Tubura farmers and identified control farmers. Execute propensity score matching (PSM) to identify control farmers that overlap with Tubura farmers with regard to their likelihood of being a Tubura farmer.

psmVars <- paste(c("female", "age", "hhsize", "total.seasons",
                   "cows", "goats", "chickens", "pigs", "sheep"),
                   collapse=" + ")
reg <- glm(as.formula(paste("client ~", psmVars, sep="")), 
           family= binomial(link="logit"), data=d)
summary(reg)    

Call:
glm(formula = as.formula(paste("client ~", psmVars, sep = "")), 
    family = binomial(link = "logit"), data = d)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.5170  -0.8509   0.0426   0.9319   2.0723  

Coefficients:
               Estimate Std. Error z value Pr(>|z|)    
(Intercept)    0.413913   0.202863   2.040   0.0413 *  
female        -0.744249   0.097219  -7.655 1.93e-14 ***
age           -0.024071   0.003368  -7.147 8.90e-13 ***
hhsize         0.036271   0.022919   1.583   0.1135    
total.seasons  0.519325   0.028386  18.295  < 2e-16 ***
cows           0.006420   0.017843   0.360   0.7190    
goats         -0.004032   0.029835  -0.135   0.8925    
chickens       0.021716   0.017745   1.224   0.2211    
pigs           0.051552   0.057656   0.894   0.3713    
sheep          0.047237   0.069853   0.676   0.4989    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 3381.1  on 2438  degrees of freedom
Residual deviance: 2573.8  on 2429  degrees of freedom
AIC: 2593.8

Number of Fisher Scoring iterations: 5
# summarize predicted probabilities
pr <- data.frame(pr_score = predict(reg, type='response'), treat = d$client)
# graph
psmGraph <- ggplot() + geom_histogram(data=subset(pr, pr$treat==1), aes(x = pr_score, y=..count.., fill=as.factor(treat)), bins=80, position = "identity") +
    geom_histogram(data=subset(pr, pr$treat==0), aes(x=pr_score, y=-..count.., fill=as.factor(treat)), bins=80, position = "identity") +
    scale_y_continuous(limits=c(-150,150)) + 
  labs(title ="PSM score overlap", x = "PSM score", y="Farmer count",
       fill="Tubura/Control")
print(psmGraph)
pdf(file=paste("output", "rw_baseline_psm_overlap.pdf", sep="/"), height=8.5, width=11)
print(psmGraph)
dev.off()
quartz_off_screen 
                2 

Interpretation We have some overlap but it’s clear that Tubura farmers occupy a different range than the identified control farmers. Let’s continue with the PSM matching process but restrict ourselves to Tubura and control farmers that meet a certain PSM matching radius.

5.0.1 Notes on PSM process:

We have to indicate a variable for matching. I’m choosing pH as we know it to be an issue in most of our operating areas and addressing soil acidity has numerous residual benefits to soil health. I’ll want to do this for all soil outcomes, however.

  • As a next step, I’d like to identify the matched subset and then regress Tubura tenure on soil outcomes. Does that make sense?
  • I chose a caliper of 0.25. I should review this process with Maya to make certain I’m following best practice. I sort of just pulled that sd figure out of the sky.
  • An initial check of the match quality using a common model indicates that the matches are poor.
# PSM prep
tr <- cbind(d$client)
x <- d[, unlist(strsplit(psmVars, " + ", fixed=T))]
y <- soilVars
# PSM
set.seed(20161102)
m <- lapply(y, function(response){
  suppressWarnings(
  mod <- Match(Y = d[,response], Tr = tr, X = reg$fitted, ties=FALSE, replace=FALSE, caliper=0.25, estimand = "ATE")    
  )
  matchRes <- MatchBalance(tr ~ d[,response], match.out=mod, nboots=500, data=d, print.level = 0)
  return(list(mod, matchRes))
})
#lapply(m, summary)

Now check the naive model approach for PSM balance.

matchRes <- do.call(rbind, lapply(1:length(m), function(model){
  val <- as.data.frame(cbind(
    standard.diff=m[[model]][[2]]$AfterMatching[[1]]$sdiff, 
    var.ratio = m[[model]][[2]]$AfterMatching[[1]]$var.ratio,
    sdiff.adj = m[[model]][[2]]$AfterMatching[[1]]$sdiff/100))
  return(val)
}))
rownames(matchRes) <- y
print(kable(matchRes))
standard.diff var.ratio sdiff.adj
m3.Ca -2.9586010 0.8621133 -0.0295860
m3.Mg -9.8036266 0.7586184 -0.0980363
pH 0.9623464 0.9756746 0.0096235
Total.N 6.2212453 1.0317110 0.0622125
Total.C 0.7591351 1.0076355 0.0075914
ExAl -1.2303827 0.9764596 -0.0123038

Interpretation: We want to see standard mean differences less than the absolute value of 0.25 (or 0.1 if we’re being conservative) and variance ratios close to 1 but certainly between 0.5 and 2.

According to the CRAN summary, sdiff is the standardized difference between the treatment and control units multiplied by 100. If I divide by 100, the values come much closer to reasonable value.

5.0.2 Let the PSM models vary

The common model approach doesn’t seem to be working for any of the variables. I’m going to rework the modeling approach so we can fit different models for each outcome upon which we’re trying to match.

d$age2 <- d$age^2
d$hhsize_age <- d$hhsize*d$age
d$hhsize2 <- d$hhsize^2
coreVars = c("female", "age", "hhsize", "own", "as.factor(cell)", "cows", "goats", "chickens", "pigs", "sheep", "black_soil", "red_soil", "sandy_soil")
psmList <- list(
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="m3.Ca"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="m3.Mg"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="pH"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="Total.N"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="Total.C"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="ExAl"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_fert"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_compost"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_seasons_leg_1"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_fallow"),
  list(tr = "client",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="logFert"),
  list(tr = "client",
       psmVars = paste(c(coreVars, "age2",
                   "hhsize2"),
                   collapse=" + "),
       y="n_seasons_leg_2"),
  list(tr = "client",
       psmVars = paste(c(coreVars, "age2",
                   "hhsize2"),
                   collapse=" + "),
       y="n_season_lime"),
  list(tr = "client",
       psmVars = paste(c("female", "age", "hhsize", "own",
                         "cows", "goats", "chickens", "pigs", "sheep",
                         "as.factor(district)", "age2", "hhsize2",
                         "hhsize_age"),
                   collapse=" + "),
       y="fert1.are"),
  # list(tr = "client",
  #      psmVars = paste(c("female", "age", "hhsize","as.factor(district)",
  #                  "cows", "goats", "chickens", "pigs", "sheep","age2", "hhsize2",
  #                        "hhsize_age"),
  #                  collapse=" + "),
  #      y="fert2.are"),
  list(tr = "client",
       psmVars = paste(c("female", "age", "hhsize","as.factor(district)",
                   "cows", "goats", "chickens", "pigs", "sheep","age2", "hhsize2",
                         "hhsize_age"),
                   collapse=" + "),
       y="compost.are")
  
)
# fertilizer application in 15B has missing values at the cell level so i include
# a district level control instead. We don't get a good fit with the current model.
# adjust!
# aggregate(d$fert1.are, by=list(d$client, d$cell), FUN=mean, na.rm=T)
# lime.are is also being finicky
# PSM
set.seed(20161102)
m <- lapply(psmList, function(listInput){
  naCheck <- unlist(strsplit(gsub("\\+", "", as.vector(listInput$psmVars))," ", fixed=TRUE))
  naCheck <- naCheck[-which(naCheck=="")]
  
  # keep complete cases of outcome variable
  k <- d[complete.cases(d[,listInput$y]),]
  k <- k[complete.cases(k[,listInput$y]),]
  
  # run glm regression:
  reg <- glm(as.formula(paste(listInput$tr, "~", listInput$psmVars, sep="")),  family= binomial(link="logit"), data=k)
  
  suppressWarnings(
  mod <- Match(Y = k[,listInput$y], Tr = k[,listInput$tr], X = reg$fitted, ties=FALSE, replace=FALSE, caliper=0.25, estimand = "ATE")   
  )
  matchRes <- MatchBalance(k[,listInput$tr] ~ k[,listInput$y], match.out=mod, nboots=500, data=k, print.level = 0)
  #print(listInput$y)
  return(list(mod, matchRes))
  
})

The models can now vary by outcome. Let’s see if we can improve our results.

matchRes <- do.call(rbind, lapply(1:length(m), function(model){
  val <- as.data.frame(cbind(
    standard.diff=m[[model]][[2]]$AfterMatching[[1]]$sdiff, 
    var.ratio = m[[model]][[2]]$AfterMatching[[1]]$var.ratio,
    sdiff.adj = m[[model]][[2]]$AfterMatching[[1]]$sdiff/100))
  return(val)
}))
namesInput <- NULL
for(i in 1:length(psmList)){
  namesInput[i] <- psmList[[i]]$y
}
rownames(matchRes) <- namesInput
print(kable(matchRes))
standard.diff var.ratio sdiff.adj
m3.Ca -6.4794848 0.8941595 -0.0647948
m3.Mg -2.5858964 0.9904604 -0.0258590
pH -5.2477889 1.0041375 -0.0524779
Total.N -1.1367538 1.0242614 -0.0113675
Total.C -0.6494876 1.0308477 -0.0064949
ExAl 4.7152699 1.0254513 0.0471527
n_season_fert 79.2241541 3.4210382 0.7922415
n_season_compost 8.4743566 0.9611436 0.0847436
n_seasons_leg_1 8.0648655 0.8951985 0.0806487
n_season_fallow 5.5545131 1.2544335 0.0555451
logFert 103.4470614 1.7397363 1.0344706
n_seasons_leg_2 -13.4710688 0.9138884 -0.1347107
n_season_lime 13.9533769 1.5456851 0.1395338
fert1.are 8.3515976 2.2500787 0.0835160
compost.are 7.8808248 1.0029775 0.0788082

Interpretation If I divide the standardized mean differences by 100, we meet the balance criteria of the standardized mean difference being close to 0 and the variance being close to 1. Let’s print out the model results to see how Tubura and control farmers compare on key soil meterics. These results should supercede the naive balance tables presented above.

We achieve acceptable balance for the soil attributes but we don’t for seasons of fertilizer use. This is isn’t entirely unexpected given that Tubura’s primary service is providing fertilizer inputs and training.

coefTable <- do.call(rbind, lapply(1:length(m), function(model){
  beta = round(m[[model]][[1]]$est.noadj,3)
  mean.Tr = round(m[[model]][[2]]$AfterMatching[[1]][[3]], 2)
  mean.Co = round(m[[model]][[2]]$AfterMatching[[1]][[4]], 2)
  pval = m[[model]][[2]]$AfterMatching[[1]][[10]][[3]] # p.value
  #pval = (1 - pnorm(abs(m[[model]][[1]]$est/m[[model]][[1]]$se.standard))) * 2
  pval = ifelse(pval < 0.001, "0.001", round(pval, 3))
  
  res = data.frame(beta, mean.Tr, mean.Co, pval)
  return(res)
}))
row.names(coefTable) <- namesInput
coefTable$pval.adj <- round(p.adjust(coefTable$pval, method="fdr"),3)
print(kable(coefTable))
beta mean.Tr mean.Co pval pval.adj
m3.Ca -40.669 796.70 837.37 0.052 0.097
m3.Mg -3.230 208.99 212.22 0.421 0.486
pH -0.032 5.46 5.49 0.097 0.146
Total.N 0.000 0.15 0.15 0.725 0.777
Total.C -0.003 2.08 2.08 0.841 0.841
ExAl 0.028 0.61 0.58 0.144 0.196
n_season_fert 2.564 3.41 0.85 0.001 0.004
n_season_compost 0.318 5.93 5.61 0.009 0.027
n_seasons_leg_1 0.214 2.29 2.07 0.014 0.035
n_season_fallow 0.095 0.69 0.59 0.069 0.115
logFert 0.825 1.19 0.36 0.001 0.004
n_seasons_leg_2 -0.427 2.61 3.04 0.001 0.004
n_season_lime 0.097 0.23 0.13 0.001 0.004
fert1.are 0.172 1.22 1.05 0.312 0.390
compost.are 7.938 52.36 44.43 0.018 0.039

I’m comparing season 1 clients to clients with more tenure in the PSM matching but I also need this variable for the threshold calculations

# 0 is new client, 1 is returning client
d$tenure.tiers <- ifelse(d$client==1 & d$total.seasons==1, 0,
                         ifelse(d$client==1 & d$total.seasons>1, 1, NA))

5.1 Calculate farmers under soil health thresholds - table 1

thresh <- d %>% group_by(client) %>% dplyr::summarize(
  count = n(),
  ph = sum(pH<5.8),
  carbon = sum(Total.C < 2),
  nitrogen = sum(Total.N < 0.1),
  calcium = sum(m3.Ca < 1056),
  magnesium = sum(m3.Mg < 148)
  #aluminum = sum(ExAl)
) %>% mutate(
  under.ph = paste(paste(round(ph/count,4)*100, "%", sep=""), " (", ph, ")", sep=""),
  under.carbon = paste(paste(round(carbon/count,4)*100,"%", sep=""), " (", carbon, ")", sep=""),
  under.nitrogen = paste(paste(round(nitrogen/count,4)*100, "%", sep=""), " (", nitrogen, ")", sep=""),
  under.calcium = paste(paste(round(calcium/count,4)*100, "%", sep=""), " (", calcium, ")", sep=""),
  under.mag = paste(paste(round(magnesium/count,4)*100,"%", sep=""), " (", magnesium, ")", sep="")
) %>% as.data.frame() 
thresh <- thresh[, c("client", names(thresh)[grep("under", names(thresh))])]
thresh <- t(thresh)
colnames(thresh) = thresh[1, ] # the first row will be the header
colnames(thresh) = c("non-client", "client")
thresh = thresh[-1, ]
write.csv(thresh, file=paste("output", "table1_rw_thresholds.csv", sep = "/"), row.names = T)

New clients vs. returning clients summaries:

newOld <- d %>% filter(!is.na(tenure.tiers)) %>% group_by(tenure.tiers) %>% 
  summarise(
  ph = round(mean(pH, na.rm=T),3),
  carbon = round(mean(Total.C, na.rm=T),3),
  nitrogen = round(mean(Total.N, na.rm=T),3),
  calcium = round(mean(m3.Ca, na.rm=T),3),
  magnesium = round(mean(m3.Mg, na.rm=T),3)
) %>% ungroup() 
newOld <- as.data.frame(t(newOld))
colnames(newOld) = newOld[1, ] # the first row will be the header
colnames(newOld) = c("New Client", "Returning Client")
newOld = newOld[-1, ]
#write.csv(newOld, file=paste(od, "rw table1 new old values.csv", sep = "/"), row.names = T)

New Clients vs. returning clients thresholds

thresh.t <- d %>% filter(!is.na(tenure.tiers)) %>%
  group_by(tenure.tiers) %>% dplyr::summarize(
  count = n(),
  ph = sum(pH<5.8),
  carbon = sum(Total.C < 2),
  nitrogen = sum(Total.N < 0.1),
  calcium = sum(m3.Ca < 1056),
  magnesium = sum(m3.Mg < 148)
  #aluminum = sum(ExAl)
) %>% mutate(
  under.ph = paste(paste(round(ph/count,4)*100, "%", sep=""), " (", ph, ")", sep=""),
  under.carbon = paste(paste(round(carbon/count,4)*100,"%", sep=""), " (", carbon, ")", sep=""),
  under.nitrogen = paste(paste(round(nitrogen/count,4)*100, "%", sep=""), " (", nitrogen, ")", sep=""),
  under.calcium = paste(paste(round(calcium/count,4)*100, "%", sep=""), " (", calcium, ")", sep=""),
  under.mag = paste(paste(round(magnesium/count,4)*100,"%", sep=""), " (", magnesium, ")", sep="")
) %>% as.data.frame() 
thresh.t <- thresh.t[, c("tenure.tiers", names(thresh.t)[grep("under", names(thresh.t))] )]
thresh.t <- t(thresh.t)
colnames(thresh.t) = thresh.t[1, ] # the first row will be the header
colnames(thresh.t) = c("new client thold", "returning client thold")
thresh.t = thresh.t[-1, ]
# combine newOld summaries and percents below percentiles
thresh.t <- cbind(newOld, thresh.t)
write.csv(thresh.t, file=paste("output", "table1_rw_thresholds_tenure.csv", sep = "/"), row.names = T)

5.2 Table 1:3 coefficients

#
write.csv(coefTable, file=paste("output", "psm coefficients.csv", sep = "/"),
          row.names = T)
# sort by the order Eric wants
t1order <- c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")
table1vars <- paste(t1order, collapse = "|")
table1 <- coefTable[grep(table1vars, rownames(coefTable)), ]
table1 <- table1[order(match(rownames(table1), t1order)),]
write.csv(table1, file=paste("output", "psm coefficients ordered for ES.csv", sep = "/"))
# 11/17 added lime
t2order <- c("n_season_fert", "n_season_compost", "n_season_lime", "n_season_fallow", "n_seasons_leg_1", "n_seasons_leg_2")
table2vars <- paste(t2order, collapse = "|")
table2 <- coefTable[grep(table2vars, rownames(coefTable)), ]
table2 <- table2[order(match(rownames(table2), t2order)),]
write.csv(table2, file=paste("output", "psm coefficients ordered for ES_agprac.csv", sep = "/"))
#table 3
t3order <- c("fert1.are", "compost.are")
table3vars <- paste(t3order, collapse = "|")
table3 <- coefTable[grep(table3vars, rownames(coefTable)), ]
table3 <- table3[order(match(rownames(table3), t3order)),]
write.csv(table3, file=paste("output", "psm coefficients table3.csv", sep = "/"))

Interpretation: Propensity score matching gives us a comparable treatment and control group. The table above shows that after matching on those characteristics, there are effectively no differences between One Acre Fund farmer and Tubura farmers on soil attributes. The unadjusted p-values show 1AF farmers to have slow levels of soil nitrogen but this finding disappears if we account for running multiple matching models.

When we expand the outcome variable set to include practice variables, we first no longer get a good propensity score match for all variables.

  • Tubura participation so strongly overlaps with fertilizer use that the comparison group is no longer comparable. The resulting model shows a significant difference in seasons of fertilizer use.
  • Tubura farmers have used compost for 0.3 more seasons than non-Tubura farmers. We don’t observe a difference in N or C levels in the soil attributes however.
  • We don’t see a difference between Tubura and non-Tubura farmers on seasons of legumes being the primary crop in the study plot.

5.3 Break out first season clients from more tenured - for appendix?

This requires us to re-run the PSM matching process, confirm that our models are sound and then output the results ala table 2 again.

psmList <- list(
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="m3.Ca"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="m3.Mg"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="pH"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="Total.N"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="Total.C"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="ExAl"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_fert"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_compost"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_seasons_leg_1"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="n_season_fallow"),
  list(tr = "tenure.tiers",
       psmVars = paste(coreVars,
                   collapse=" + "),
       y="logFert"),
  list(tr = "tenure.tiers",
       psmVars = paste(c(coreVars, "age2",
                   "hhsize2"),
                   collapse=" + "),
       y="n_seasons_leg_2"),
  list(tr = "tenure.tiers",
       psmVars = paste(c(coreVars, "age2",
                   "hhsize2"),
                   collapse=" + "),
       y="n_season_lime"),
  list(tr = "tenure.tiers",
       psmVars = paste(c("female", "age", "hhsize", "own",
                         "cows", "goats", "chickens", "pigs", "sheep",
                         "as.factor(district)", "age2", "hhsize2",
                         "hhsize_age"),
                   collapse=" + "),
       y="fert1.are"),
  # list(tr = "tenure.tiers",
  #      psmVars = paste(c("female", "age", "hhsize","as.factor(district)",
  #                  "cows", "goats", "chickens", "pigs", "sheep","age2", "hhsize2",
  #                        "hhsize_age"),
  #                  collapse=" + "),
  #      y="fert2.are"),
  list(tr = "tenure.tiers",
       psmVars = paste(c("female", "age", "hhsize","as.factor(district)",
                   "cows", "goats", "chickens", "pigs", "sheep","age2", "hhsize2",
                         "hhsize_age"),
                   collapse=" + "),
       y="compost.are")
  
)
# fertilizer application in 15B has missing values at the cell level so i include
# a district level control instead. We don't get a good fit with the current model.
# adjust!
# aggregate(d$fert1.are, by=list(d$client, d$cell), FUN=mean, na.rm=T)
# lime.are is also being finicky
# PSM
set.seed(20161102)
mappend <- lapply(psmList, function(listInput){
  naCheck <- unlist(strsplit(gsub("\\+", "", as.vector(listInput$psmVars))," ", fixed=TRUE))
  naCheck <- naCheck[-which(naCheck=="")]
  
  # keep complete cases of outcome variable
  k <- d[complete.cases(d[,listInput$y]),]
  k <- k[complete.cases(k[,listInput$y]),]
  k <- k[complete.cases(k[,listInput$tr]),]
  
  # run glm regression:
  reg <- glm(as.formula(paste(listInput$tr, "~", listInput$psmVars, sep="")),  family= binomial(link="logit"), data=k)
  
  suppressWarnings(
  mod <- Match(Y = k[,listInput$y], Tr = k[,listInput$tr], X = reg$fitted, ties=FALSE, replace=FALSE, caliper=0.25, estimand = "ATE")   
  )
  matchRes <- MatchBalance(k[,listInput$tr] ~ k[,listInput$y], match.out=mod, nboots=500, data=k, print.level = 0)
  #print(listInput$y)
  return(list(mod, matchRes))
  
})
matchRes <- do.call(rbind, lapply(1:length(mappend), function(model){
  val <- as.data.frame(cbind(
    standard.diff=m[[model]][[2]]$AfterMatching[[1]]$sdiff, 
    var.ratio = m[[model]][[2]]$AfterMatching[[1]]$var.ratio,
    sdiff.adj = m[[model]][[2]]$AfterMatching[[1]]$sdiff/100))
  return(val)
}))
namesInput <- NULL
for(i in 1:length(psmList)){
  namesInput[i] <- psmList[[i]]$y
}
rownames(matchRes) <- namesInput
print(kable(matchRes))
standard.diff var.ratio sdiff.adj
m3.Ca -6.4794848 0.8941595 -0.0647948
m3.Mg -2.5858964 0.9904604 -0.0258590
pH -5.2477889 1.0041375 -0.0524779
Total.N -1.1367538 1.0242614 -0.0113675
Total.C -0.6494876 1.0308477 -0.0064949
ExAl 4.7152699 1.0254513 0.0471527
n_season_fert 79.2241541 3.4210382 0.7922415
n_season_compost 8.4743566 0.9611436 0.0847436
n_seasons_leg_1 8.0648655 0.8951985 0.0806487
n_season_fallow 5.5545131 1.2544335 0.0555451
logFert 103.4470614 1.7397363 1.0344706
n_seasons_leg_2 -13.4710688 0.9138884 -0.1347107
n_season_lime 13.9533769 1.5456851 0.1395338
fert1.are 8.3515976 2.2500787 0.0835160
compost.are 7.8808248 1.0029775 0.0788082
coefTable.append <- do.call(rbind, lapply(1:length(mappend), function(model){
  beta = round(mappend[[model]][[1]]$est.noadj,3)
  mean.Tr = round(mappend[[model]][[2]]$AfterMatching[[1]][[3]], 2)
  mean.Co = round(mappend[[model]][[2]]$AfterMatching[[1]][[4]], 2)
  pval = mappend[[model]][[2]]$AfterMatching[[1]][[10]][[3]] # p.value
  #pval = (1 - pnorm(abs(m[[model]][[1]]$est/m[[model]][[1]]$se.standard))) * 2
  pval = ifelse(pval < 0.001, "0.001", round(pval, 3))
  
  res = data.frame(beta, mean.Tr, mean.Co, pval)
  return(res)
}))
row.names(coefTable.append) <- namesInput
coefTable.append$pval.adj <- round(p.adjust(coefTable.append$pval, method="fdr"),3)
names(coefTable.append) <- c("beta", "Old Clients", "New Clients", "pval", "pval.adj")
print(kable(coefTable.append))
beta Old Clients New Clients pval pval.adj
m3.Ca -46.924 824.09 871.02 0.425 0.708
m3.Mg -3.113 208.98 212.09 0.79 0.846
pH -0.005 5.49 5.49 0.924 0.924
Total.N -0.007 0.15 0.16 0.021 0.105
Total.C -0.071 2.04 2.11 0.089 0.270
ExAl 0.028 0.61 0.58 0.605 0.846
n_season_fert 1.640 3.55 1.91 0.001 0.008
n_season_compost 0.560 5.80 5.24 0.09 0.270
n_seasons_leg_1 0.213 2.28 2.07 0.389 0.708
n_season_fallow 0.044 0.63 0.59 0.739 0.846
logFert 0.473 1.31 0.83 0.001 0.008
n_seasons_leg_2 0.102 2.36 2.26 0.702 0.846
n_season_lime 0.050 0.23 0.18 0.305 0.654
fert1.are 0.153 1.35 1.20 0.715 0.846
compost.are 7.693 48.77 41.08 0.243 0.607
# sort by the order Eric wants
t1order.a <- c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl")
table1vars.a <- paste(t1order.a, collapse = "|")
table1.a <- coefTable.append[grep(table1vars.a, rownames(coefTable.append)), ]
table1.a <- table1.a[order(match(rownames(table1.a), t1order.a)),]
write.csv(table1.a, file=paste("output", "psm coefficients ordered for ES-appendix.csv", sep = "/"))
# 11/17 added lime
t2order.a <- c("n_season_fert", "n_season_compost", "n_season_lime", "n_season_fallow", "n_seasons_leg_1", "n_seasons_leg_2")
table2vars.a <- paste(t2order.a, collapse = "|")
table2.a <- coefTable.append[grep(table2vars.a, rownames(coefTable.append)), ]
table2.a <- table2.a[order(match(rownames(table2.a), t2order.a)),]
write.csv(table2.a, file=paste("output", "psm coefficients ordered for ES_agprac-appendix.csv", sep = "/"))
#table 3
t3order.a <- c("fert1.are", "compost.are")
table3vars.a <- paste(t3order.a, collapse = "|")
table3.a <- coefTable.append[grep(table3vars.a, rownames(coefTable.append)), ]
table3.a <- table3.a[order(match(rownames(table3.a), t3order.a)),]
write.csv(table3.a, file=paste("output", "psm coefficients table3-appendix.csv", sep = "/"))

5.4 Study group balance post-match

See here for some guidance on hwo to use weights to reconstruct the group balance following the matches.

See here for weighted t.test documentation

suppressMessages(library(weights))
tableVars <- c("age", "female", "hhsize", "own")
postMatch <- do.call(rbind, lapply(1:length(m), function(model){
  
  innerPost <- do.call(rbind, lapply(tableVars, function(x){
  
  mean.t = weighted.mean(d[m[[model]][[1]]$index.treated,][,x], m[[model]][[1]]$weights)
  mean.c = weighted.mean(d[m[[model]][[1]]$index.control,][,x], m[[model]][[1]]$weights)
  
  # combined data
  dm <- as.data.frame(rbind(d[m[[model]][[1]]$index.treated,], 
              d[m[[model]][[1]]$index.control,]))
  
  test = wtd.t.test(d[m[[model]][[1]]$index.treated,][,x], 
                    d[m[[model]][[1]]$index.control,][,x],
                    weight=m[[model]][[1]]$weights,
                    samedata=TRUE)
  
  return(data.frame(model.num = model, 
                    outcome=x, 
                    tr=mean.t, 
                    contr = mean.c,
                    pval = test$coefficients[3][[1]]))
  }))
  
  return(innerPost)
}))
write.csv(postMatch, file=paste("output", "rw post match covars.csv", sep = "/"),
          row.names = F)

5.5 Models with PSM matched farmers

Per Robert’s suggestion, now that we’ve matched Tubura and non-Tubura farmers, let’s assess the severity of Tubura tenure on key soil health outcomes.

tenureTab.add <- lapply(1:length(m), function(model){
  
  dm <- as.data.frame(rbind(d[m[[model]][[1]]$index.treated,], 
              d[m[[model]][[1]]$index.control,]))
  dm$client_tenure <- dm$client*dm$total.seasons
  mod <- lm(as.formula(paste("dm[,psmList[[model]]$y] ~", "total.seasons + as.factor(cell)", sep ="")), data=dm)
  return(mod)
})
# add tenured>=3 to d if we want to run this as a binary comparison
# tenureTab.binary <- lapply(1:length(m), function(model){
#   
#   dm <- as.data.frame(rbind(d[m[[model]][[1]]$index.treated,], 
#               d[m[[model]][[1]]$index.control,]))
#   dm$client_tenure <- dm$client*dm$total.seasons
#   mod <- lm(as.formula(paste("dm[,psmList[[model]]$y] ~", "tenured + as.factor(cell)", sep ="")), data=dm)
#   return(mod)
# })
# modNames <- c("Calcium", "Magnesium", "pH", "Nitrogen", "Carbon", "Seasons fertilizer", "Seasons compost", "Seasons legumes", "Seasons fallow", "Fertilizer (log)", "Season Sec. legumes", "Seasons Lime", "Fertilizer/Are", "Compost/are")
modNames <- unlist(lapply(psmList, function(x){
  return(x$y)
}))
suppressWarnings(
stargazer(tenureTab.add, type="html", 
          title = "2016A Rwanda Soil Health Baseline - PSM Tenure",
          covariate.labels = "One Acre Fund Tenure", 
          dep.var.labels = "",
          #column.labels = modNames,
          #column.labels = c(gsub("m3.", "", as.vector(namesInput))),
          notes = "Includes FE for cell",
          omit=c("cell"), out=paste(od, "rw_baseline_matched_tenure.htm", sep="/"))
)
plm.tenure <- function(x){
  
  intercept = x$coefficients[[1]]
  beta = round(x$coefficients[[2]],3)
  int.pval = summary(x)$coefficients[1,4]
  int.pval = ifelse(int.pval < 0.001, "< 0.001", round(as.numeric(int.pval),3))
  beta.pval = summary(x)$coefficients[2,4]
  beta.pval = ifelse(beta.pval < 0.001, "< 0.001", round(as.numeric(beta.pval),3))
  res = data.frame(intercept, int.pval, beta, beta.pval, stringsAsFactors = F)
  
  return(res)  
}
tenure.reg <- do.call(rbind, lapply(tenureTab.add, function(x){
  plm.tenure(x)
}))
rownames(tenure.reg) <- modNames
t6order <- c("cows", "pigs", "sheep", "goats", "chickens")
table6vars <- paste(t6order, collapse = "|")
rw.table6 <- output[grep(table6vars, rownames(output)), ]
rw.table6 <- rw.table6[order(match(rownames(rw.table6), t4order)),]
ESorder <- c("pH", "Total.C", "Total.N", "m3.Ca", "m3.Mg", "ExAl", "n_season_fert",
             "logFert", "n_season_compost", "n_seasons_leg_1", "n_season_fallow")
ESorder.grep <- paste(ESorder, collapse = "|")
tenure.reg <- tenure.reg[grep(ESorder.grep,rownames(tenure.reg)),]
tenure.reg <- tenure.reg[order(match(row.names(tenure.reg), ESorder)),]
write.csv(tenure.reg, file=paste(od, "table13_reg.csv", sep = "/"), row.names=T)

Interpretation: Using a PSM matched sample, the models above assess the effects of additional years of farming with Tubura. Numerous control farmers have also been Tubura farmers in previous seasons. Thus, I’m keeping the model simple instead of adding a client*tenure interaction. We can easily test that as well though.

6 Soil Clusters

6.1 Heirarchical Clustering

I draw from Robert’s example in the BGMS soil response notebook.

Scale soil variables to remove units as an issue.

scaledSoil <- scale(d[,soilVars])
clustMethods <- c("ward.D2", "single", "complete", "average", "mcquitty", "median", "centroid")
invisible(sapply(clustMethods, FUN=function(method) {
    plot(hclust(dist(scaledSoil), method=method), main=method, ylab="", label=FALSE)
}))

Note: I’m honestly not certain what we’re looking for in these outputs. Look for a balanced tree? Ward looks pretty balanced.

hc <- hclust(dist(scaledSoil), method="ward.D2")
d$hc4 <- cutree(hc, k=4)
hcCentroids <- aggregate(list(d[,soilVars]), by=list(cluster=d$hc4), FUN=mean)
hcTable <- rbind(t(table(d$hc4)),
      t(round(hcCentroids, 2)))
print(kable(hcTable))
1 2 3 4
726.00 806.00 503.00 404.00
cluster 1.00 2.00 3.00 4.00
m3.Ca 709.60 329.10 910.03 1893.42
m3.Mg 210.68 111.16 236.64 371.69
pH 5.63 4.83 5.63 6.33
Total.N 0.12 0.16 0.17 0.17
Total.C 1.65 2.32 2.20 2.23
ExAl 0.24 1.30 0.33 0.12

6.2 K-means clustering

K-means clustering is a typical unsupervised learning algorithm. We start by identifying the number of clusters we should tell the formula to look for.

numClusters <- 2:10
plot(numClusters,
     sapply(numClusters, FUN=function(k) { sum(kmeans(scaledSoil, centers=k, nstart=20, iter.max=20)$withinss) }),
     type="b", ylab="average within cluster sum of squared error")

We’re looking for the bend in the graph. It seems to come at 3 but we can try both 3 and 4 and see how they match with our understanding of AEZ and environmental conditions.

set.seed(20161125)
clusters <- 4
d$km4 <- kmeans(scaledSoil, centers=clusters, nstart=20, iter.max=20)$cluster
kmCentroids <- aggregate(list(d[,soilVars]), by=list(cluster=d$km4), FUN=mean)
kmTable <- rbind(t(table(d$km4)),
      t(round(kmCentroids, 2)))
print(kable(kmTable))
1 2 3 4
462.00 669.00 972.00 336.00
cluster 1.00 2.00 3.00 4.00
m3.Ca 1761.59 385.48 846.72 322.50
m3.Mg 373.34 130.16 224.14 102.13
pH 6.17 4.97 5.75 4.76
Total.N 0.19 0.14 0.13 0.19
Total.C 2.38 2.01 1.75 2.76
ExAl 0.19 0.94 0.20 1.56

These clusters are roughly equally sized. Robert used another method, partitioning around Mediods, to better account for outliers that were causing the algorithm to create a small additional cluster.

Give the clusters brief descriptions

d$km4 <- factor(d$km4, labels=c("pH=5.9, C=1.7", 
                                "pH=6.1,C=2.4", 
                                "pH=5.1, C=2",
                                "pH=4.8, C=2.6"))

6.3 Mappaing the location of the clusters

suppressMessages(library(ggmap))
qmplot(lon, lat, data=d, color=d$km4)
Using zoom = 9...
Source : http://tile.stamen.com/toner-lite/9/297/258.png
Source : http://tile.stamen.com/toner-lite/9/298/258.png
Source : http://tile.stamen.com/toner-lite/9/299/258.png
Source : http://tile.stamen.com/toner-lite/9/297/259.png
Source : http://tile.stamen.com/toner-lite/9/298/259.png
Source : http://tile.stamen.com/toner-lite/9/299/259.png
Source : http://tile.stamen.com/toner-lite/9/297/260.png
Source : http://tile.stamen.com/toner-lite/9/298/260.png
Source : http://tile.stamen.com/toner-lite/9/299/260.png

#qmplot(lon, lat, data=d, color=as.factor(hc4))
crdref <- CRS('+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0')
e <- d[!is.na(d$lon),]
ss <- SpatialPointsDataFrame(coords = e[, c("lon", "lat")], data=e, proj4string = crdref)
pal <- colorFactor("RdYlBu", domain=unique(ss$km4))
leaflet() %>% addTiles() %>%
  setView(lng=rwanda$longitude, lat=rwanda$latitude, zoom=8) %>%
  addCircleMarkers(lng=ss$lon, lat=ss$lat, 
                   #radius= ifelse(ss$km4==1, 10,6),
                   color = pal(ss$km4), clusterOptions = markerClusterOptions(disableClusteringAtZoom=11, spiderfyOnMaxZoom=FALSE)) %>% 
  addLegend(pal = pal, values=unique(ss$km4), title = "Clusters ")

NA

6.4 Soil clusters by altitude and AEZ

ggplot(d, aes(x=km4, y=alt)) + geom_boxplot()

6.4.1 Soil clusters by altitude and rainfall

Try this when we have aWhere data

6.5 Compare clusters to AEZ

print(kable(table(d$km4, d$aez)))
Central Plateau Congo Nile East Lake Kivu
pH=5.9, C=1.7 65 11 291 95
pH=6.1,C=2.4 154 268 57 190
pH=5.1, C=2 345 82 253 292
pH=4.8, C=2.6 30 235 13 58

We’re not seeing close alignment between clusters and AEZ. This is probably because AEZ are a mix of altitude, rainfall, temperature and soil. When we have weather data we can try to cluster again and see if we’re more closely aligned.

Alternatively, this could be initial evidence that the AEZ designations are not capturing and separating variation very well. I think we need to start with more data though.

7 Mapping

7.1 Soil health maps for soil health study areas

#crdref <- CRS('+proj=longlat +datum=WGS84')
crdref <- CRS('+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0')
e <- d[!is.na(d$lon),]
ss <- SpatialPointsDataFrame(coords = e[, c("lon", "lat")], data=e, proj4string = crdref)
rw <- getData("GADM", country='RW', level=3, path = "/Users/mlowes/drive/optimized_agronomy/soil/soil_health_study/data") # the higher the number, the higher the resolution
#ext.rw.ss matches points with spatial polygons in rw
ext.rw.ss <- extract(rw[, "NAME_3"], ss)
Loading required namespace: rgeos
ss$spatialname <- ext.rw.ss$NAME_3
frw <- fortify(rw, region="NAME_3")
#for soil features
ss.soil <- aggregate(ss@data[,soilVars], by=list(ss@data$spatialname), function(x){
  mean(x, na.rm=T)
})
plotReady <- dplyr::left_join(frw, ss.soil, by=c("id"="Group.1"))
ss.count <- ss@data %>% group_by(spatialname) %>% dplyr::summarize(
  count = n()
) %>% as.data.frame()
countPlot <- dplyr::left_join(frw, ss.count, by=c("id"="spatialname"))

Generate district density summaries for Nathaniel to pair with the map

districtDensity <- d %>% group_by(district) %>% dplyr::summarize(
  count = n()
) %>% as.data.frame()
cellDensity <- d %>% group_by(district, cell) %>% dplyr::summarize(
  count = n()
) %>% as.data.frame()
write.csv(districtDensity, file=paste("output", "rw district density table.csv", sep = "/"),
          row.names=T)
write.csv(cellDensity, file=paste("output", "rw cell density table.csv", sep = "/"),
          row.names=T)

7.2 Static map of sampling density

library(RColorBrewer)
mapRes <-  ggplot(countPlot, aes(x=long, y=lat, group=group)) + geom_path() + 
  geom_polygon(aes(fill=countPlot$count)) + 
  #coord_map(xlim = c(33.5, 36),ylim = c(-2, 1.75)) +
  scale_fill_gradientn(colours = brewer.pal(9,"Reds"), # define colors
        name = "Sampling Density",
        guide = guide_colorbar(legend.direction = "vertical")) + 
  theme_bw() + 
  labs(title="Rwanda soil health study sampling density", x = "Longitude", y="Latitude")
print(mapRes)
pdf(paste(od, "rwanda sampling density.pdf", sep = "/"), width=11, height=8.5)
print(mapRes)
dev.off()
quartz_off_screen 
                2 

7.3 Plot simple summary of soil characteristics

Consider revising these maps to a smaller greographic unit. Add the name of the location for uninitiated users.

library(RColorBrewer)
mapList <- list()
for(i in 1:length(soilVars)){
mapRes <-  ggplot(plotReady, aes(x=long, y=lat, group=group)) + geom_path() + 
  geom_polygon(aes(fill=plotReady[,soilVars[i]])) + 
  scale_fill_gradientn(colours = rev(brewer.pal(9,"Reds")), # define colors
                       name = soilVars[i],
                       guide = guide_colorbar(legend.direction = "vertical")) + 
  theme_bw() + 
  labs(title=paste("Rwanda long term soil health baseline - 2016", soilVars[i], sep= " "), x = "Longitude", y="Latitude")
mapList[[i]] <- mapRes
print(mapRes)
} 

# This is a small experiment to combine raster (spdf) and leaflet and be able to access the data in the raster interactively.
#mapLayer <- sp::merge(rw, ss.soil, by.x="NAME_3", by.y="Group.1")

# fill = T, fillOpacity = 0.7, fillColor = d.fill, 
#         stroke = T, color = "white", weight = 2, dashArray = 3, 
#         opacity = 0.5, popup = county.tt(d)

# leaflet(mapLayer) %>% addTiles() %>%
#   setView(lng=rwanda$longitude, lat=rwanda$latitude, zoom=8) %>%
#   addPolygons(
#     stroke = TRUE, opacity=0.2, smoothFactor = 0.5, 
#     fillColor=mapLayer$pH, fillOpacity = 0.5)
pdf(file=paste(md, "rw_shs_baseline_soil_maps.pdf", sep = "/"), width=11, height=8.5)
Error in paste(md, "rw_shs_baseline_soil_maps.pdf", sep = "/") : 
  object 'md' not found

7.4 Interpolations of soil health values

Interpolate soil health values for full operating area using soil health study values. We want to eventually add all Rwandan soil values into a single dataset to update and hone these values. See here for more guidanace

note:

  • Layer on the Tubura sites to the interpolated map.
  • Use leaflet so you can zoom in and out more easily.
  • Make it a raster layer that you can click on understand the values at different locations and the name of the location. I’ll need to make this a shiny app to have that functionality

The code below will run 5 K-fold cross validation to compare interpolation models. The output will be fed into the interpolate leaflet code below.

Check that I’m handling the projection correctly with Robert

Now loop over the variables of interest where x is the soilVar variable.

7.5 Interpolated soil maps

Print out the interpolated values for inclusion in the report.

pdf(file=paste(md, "rw_shs_bl_interpolation_soil_vars.pdf", sep = '/'), width=11, height=8.5)
lapply(soilVars, function(x) {
  m <- Tps(coordinates(ss), ss@data[,x])
  # make raster layer with model, raster is rwanda empty raster, model is m
  tps <- interpolate(r, m)
  tps <- crop(tps, rw)
  tps <- raster::mask(tps, rw) # cuts the tps raster down to the rw boundaries
  x <- gsub("m3.", "", x)
  
suppressMessages(
  outPlot <- invisible(print(
  plot(tps, main= paste("Soil TPS Interpolation ", x, sep="- ")),
  plot(rw, add=T, na.print=NULL)
  ))
)
})
NULL
NULL
NULL
NULL
NULL
NULL
[[1]]
NULL

[[2]]
NULL

[[3]]
NULL

[[4]]
NULL

[[5]]
NULL

[[6]]
NULL
dev.off()
null device 
          1 

–end

LS0tCnRpdGxlOiAiUndhbmRhIEJhc2VsaW5lIFNvaWwgSGVhbHRoIFN0dWR5IFJlcG9ydCIKYXV0aG9yOiAiTWF0dCBMb3dlcyIKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJUIgJWQsICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDYKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CnJtKGxpc3Q9bHMoKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgT2JqZWN0aXZlClN1bW1hcnkgb2YgdGhlIGRlbW9ncmFwaGljcyBhbmQgc29pbCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIFJ3YW5kYSBsb25nIHRlcm0gc29pbCBoZWFsdGggc3R1ZHkuCgojIyBTdHVkeSBtZXRob2RvbG9neQpBZGQgaW4gZGV0YWlscyBhbmQgbGlua3Mgb24gc3R1ZHkgbWV0aG9kb2xvZ3kgaGVyZS4KCmBgYHtyIGRhdGF9CmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZHBseXIpKQpsaWJyYXJ5KHNwKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkocmdkYWwpKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZGlzbW8pKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RhcmdhemVyKSkKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KFhNTCkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KG1hcHRvb2xzKSkKbGlicmFyeShhdXRvbWFwKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoUlN0YXRhKSkKc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGZpZWxkcykpCmxpYnJhcnkoZ3N0YXQpCmxpYnJhcnkoaHRtbHRvb2xzKQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoTWF0Y2hpbmcpKQpsaWJyYXJ5KHJlc2hhcGUyKQpvcHRpb25zKCJSU3RhdGEuU3RhdGFWZXJzaW9uIiA9IDEyKQpvcHRpb25zKCJSU3RhdGEuU3RhdGFQYXRoIiA9ICIvQXBwbGljYXRpb25zL1N0YXRhL1N0YXRhU0UuYXBwL0NvbnRlbnRzL01hY09TL3N0YXRhLXNlIikKI2Nob29zZVN0YXRhQmluKCIvQXBwbGljYXRpb25zL1N0YXRhL1N0YXRhU0UuYXBwL0NvbnRlbnRzL01hY09TL3N0YXRhLXNlIikKYGBgCgpUaGUgd29ya2luZyBkaXJlY3Rvcnkgb2JqZWN0cyBhcmUgZnJvbSB0aGUgb3JpZ2luYWwgYmFzZWxpbmUgYW5hbHlzaXMuIEkndmUgdXBkYXRlZCB0aGlzIHRvIHB1bGwgZnJvbSBhIHN0YWJsZSB3b3JraW5nIGRpcmVjdG9yeSAoNS80LzE3KQoKYGBge3IgZGlyZWN0b3J5fQojIHdkIDwtICIvVXNlcnMvbWxvd2VzL2RyaXZlL29wdGltaXplZF9hZ3Jvbm9teS9zb2lsL3NvaWxfaGVhbHRoX3N0dWR5L2RhdGEvcncgYmFzZWxpbmUiCiMgZGQgPC0gcGFzdGUod2QsICJkYXRhIiwgc2VwID0gIi8iKQojIG9kIDwtIHBhc3RlKHdkLCAib3V0cHV0Iiwgc2VwPSIvIikKIyBtZCA8LSBwYXN0ZSh3ZCwgIm1hcHMiLCBzZXA9Ii8iKQpkcml2ZSA8LSAifi9kcml2ZS9yX2hlbHAvNF9vdXRwdXQvc3RhdGlzdGljYWxfdGVzdF9vdXRwdXRzIgpvZCA8LSAib3V0cHV0IiAjIGNvbWUgYmFjayBhbmQgdXBkYXRlIHRoaXMgbGF0ZXIuIEkgZ290IGFib3V0IGhhbGYgd2F5IHRocm91Z2g/Cm1kIDwtICJtYXBzIgoKI2xvYWQgZGF0YToKIyBUaGlzIGRhdGEgaXMgYmVpbmcgZHJhd24gZnJvbSB0aGUgU29pbCBsYWIgcmVwb3NpdG9yeS4gSXQgaGFzIHRoZSBiYXNlbGluZSBkYXRhIHdpdGggaXQKIyBkIDwtIHJlYWQuY3N2KHBhc3RlKGRkLCAiUndhbmRhX3Noc19jb21tY2FyZV9zb2lsX2RhdGFfZmluYWwuY3N2Iiwgc2VwPSIvIiksICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQpgYGAKCiMgRGF0YSBQcmVwCgojIyBDb21iaW5lIHN1cnZleSBhbmQgc29pbCBkYXRhClJlcGxpY2F0ZSBBbGV4J3MgYW5kIEVtbWFudWVsJ3MgbWVyZ2UgcHJvY2VzcyB1c2luZyAiSWRlbnRpZmllcnMgd2l0aCBTU05fZmluYWwiIHByb3ZpZGVkIGJ5IEVtbWFudWVsLiBJIHVzZSB0aGUgUlN0YXRhIHBhY2thZ2UgZm91bmQgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9sYnJhZ2xpYS9SU3RhdGEpCmBgYHtyfQojIEknbSByZXBsaWNhdGluZyBBbGV4J3MgZG8gZmlsZSBoZXJlLgpzdGF0YSgibWVyZ2Vfc2hzLmRvIikKYGBgCgpJbXBvcnQgdGhlIHJlc3VsdHMgb2YgQWxleCdzIGRvIGZpbGUuCgpgYGB7cn0KZCA8LSByZWFkLmNzdigiZGF0YS9yd2FuZGFfc2hzX2Jhc2VsaW5lX2RhdGEuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCklkZW50aWZpZXJzIDwtIHJlYWQuY3N2KCJkYXRhL0lkZW50aWZpZXJzIHdpdGggU1NOX2ZpbmFsLmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKd2V0Q2hlbSA8LSByZWFkLmNzdihwYXN0ZSgiL1VzZXJzL21sb3dlcy9kcml2ZS9qdXB5dGVyL1J3YW5kYSBBY2lkaXR5IiwgIk9yaWdpbmFsIGNoZW1fcndhbmRhX3Nocy5jc3YiLCBzZXAgPSAiLyIpKQpgYGAKCgoKYGBge3J9CmQgPC0gbGVmdF9qb2luKGQsIElkZW50aWZpZXJzLCBieT0ic2FtcGxlX2lkIikKCiMgbm93IGltcG9ydCB0aGUgc29pbCByZXN1bHRzIGFuZCBtZXJnZSB3aXRoIHN1cnZleSBkYXRhCnNvaWxEaXIgPC0gbm9ybWFsaXplUGF0aChmaWxlLnBhdGgoIi4uIiwgIi4uIiwgIk9BRiBTb2lsIExhYiBGb2xkZXIiLCAiUHJvamVjdHMiLCAicndfc2hzX2Jhc2VsaW5lIiwgIjRfcHJlZGljdGVkIiwgIm90aGVyX3N1bW1hcmllcyIpKQoKc29pbFJGIDwtIHJlYWQuY3N2KGZpbGU9cGFzdGUoc29pbERpciwgImNvbWJpbmVkLXByZWRpY3Rpb25zLWluY2x1ZGluZy1iYWQtb25lcy5jc3YiLCBzZXA9Ii8iKSkKCm9yZ1NvaWxOYW1lcyA8LSByZWFkLmNzdigiZGF0YS9yd3Noc19yZnJlc3VsdHMuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICNvbGQgZnJvbSBvcmlnaW5hbCB2YXJpYWJsZSB3b3JrCm5hbWVzKG9yZ1NvaWxOYW1lcylbbmFtZXMob3JnU29pbE5hbWVzKT09IlgiXSA8LSAiU1NOIgpgYGAKCk1hdGNoIG5ldyBzb2lsIG1vZGVsIG91dHB1dHMgdG8gdmFyaWFibGUgbmFtZXMgZnJvbSBvcmlnaW5hbCBhbmFseXNpcyBpbiAyMDE2IGFuZCBqb2luIHNvaWwgdmFyaWFibGVzIHdpdGggc3VydmV5IGRhdGEKCmBgYHtyfQpzb2lsIDwtIHNvaWxSRiAlPiUgZHBseXI6OnNlbGVjdChTU04sIENhbGNpdW0sIE1hZ25lc2l1bSwgcEgsIFRvdGFsLk5pdHJvZ2VuLCBPcmdhbmljLkNhcmJvbiwgRXhjaGFuZ2VhYmxlLkFsdW1pbml1bSkgJT4lCiAgcmVuYW1lKAogICAgbTMuQ2EgPSBDYWxjaXVtLAogICAgbTMuTWcgPSBNYWduZXNpdW0sCiAgICBUb3RhbC5OID0gVG90YWwuTml0cm9nZW4sCiAgICBUb3RhbC5DID0gT3JnYW5pYy5DYXJib24sCiAgICBFeEFsID0gRXhjaGFuZ2VhYmxlLkFsdW1pbml1bQogICkgJT4lIG11dGF0ZSgKICAgIFNTTiA9IGFzLmNoYXJhY3RlcihTU04pCiAgKQoKZCA8LSBsZWZ0X2pvaW4oZCwgc29pbCwgYnk9IlNTTiIpCmBgYAoKCiMjIENsZWFuaW5nIGJhc2VsaW5lIHZhcmlhYmxlcwoKTm93IGxldCdzIHN0YXJ0IGNsZWFuaW5nIHRoZSBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMKYGBge3IgbmFtZS5jbGVhbmluZ30KZFtkPT0iLS0tIl0gPC0gTkEKCm5hbWVzKGQpIDwtIGdzdWIoInRleHRfZmluYWxfcXVlc3Rpb25zIiwgIiIsIG5hbWVzKGQpKQpuYW1lcyhkKSA8LSBnc3ViKCJpbnRyb19jaGFtcF9lY2hhbnRpbGxvbiIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1YigiZGVtb2dyYXBoaWNfaW5mbyIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1Yigib3RoZXJfaW5wdXRzXyIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1YigiY3JvcDFfMTViX2lucHV0cyIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1YigiY3JvcDJfMTViX2lucHV0cyIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1YigiXjE1YiIsICIiLCBuYW1lcyhkKSkKbmFtZXMoZCkgPC0gZ3N1YigiaGlzdG9yaWNhbF9pbnRybyIsICIiLCBuYW1lcyhkKSkKCm5hbWVzKGQpW25hbWVzKGQpPT0iZmllbGRfZGltIl0gPC0gImZpZWxkX2RpbTEiCm5hbWVzKGQpW25hbWVzKGQpPT0idjUxIl0gPC0gImZpZWxkX2RpbTIiCmBgYAoKQWRkcmVzcyB1bnVzdWFsIGZpZWxkIHNpemVzCgpgYGB7cn0KZ2dwbG90KGQsIGFlcyh4PWZpZWxkX2RpbTEsIHk9ZmllbGRfZGltMikpICsgZ2VvbV9wb2ludCgpCmBgYAoKSXQgc2VlbXMgcmVhbGx5IHVubGlrZWx5IHRoYXQgZmllbGRzIGFyZSAyMDAgbWV0ZXJzIGxvbmcgd2hpbGUgb25seSBiZWluZyAyMCBtZXRlcnMgd2lkZS4gSSBkb24ndCBrbm93IGhvdyB0byBjaGVjayB0aGlzIHRob3VnaC4gCgpgYGB7cn0KIyBjbGVhbiBmaWVsZCBkaW1lbnNpb25zIGhlcmUgLSB3aW5zb3IgdGhlIHZhbHVlcyB0byBzb21ldGhpbmcgcmVhc29uYWJsZS4KZFsoZCRmaWVsZF9kaW0xPj0xMDAgfCBkJGZpZWxkX2RpbTI+PTEwMCkgJiAhaXMubmEoZCRmaWVsZF9kaW0xKSwgYygiZmllbGRfZGltMSIsICJmaWVsZF9kaW0yIildCmBgYAoKClRha2UgY2FyZSBvZiBkZW1vZ3JhcGhpYyBkYXRhIGZvcm1hdHRpbmcgaXNzdWVzCmBgYHtyIGZvcm1hdHRpbmd9CiMgZGVhbCB3aXRoIG5hbWVzIGFuZCBkcm9wIHVubmVjZXNzYXJ5IHZhcmlhYmxlcwpkIDwtIGQgJT4lIAoJZHBseXI6OnNlbGVjdCgtYyhyb3dudW1iZXIsIGluZm9mb3JtaWQsIGludHJvZHVjdGlvbmRfYWNjZXB0LCBwaG90bywKCQlpbmZvY29tcGxldGVkX3RpbWUsIAoJCWVudW1lcmF0b3JfbmFtZSwgY29udGFpbnMoInBob25lIiksIGZhcm1lcl9uYW1lLCBmYXJtZXJzdXJuYW1lLCBmYXJtZXJuYW1lLAoJCWRfcmVzcG9uZGVudCwgYWRkaXRpb25hbHNhbXBsZXBhY2tlZGFuZHNlbnR0b2wsIGFkZGl0aW9uYWxzYW1wbGVyZXF1ZXN0ZWRmcm9tbGFiLAoJCWRhdGVkcnlpbmdjb21wbGV0ZWlmbmVjZXNzYXJ5LCBkcmllZGluZGlzdHJpY3RpZm5lY2Vzc2FyeSwgc2VudHRvaHF5bywKCQljb2xsZWN0ZWRpbmRpc3RyaWN0eW8sIGV4Y2Vzc3N0b3JlZGF0aHFfLCByZWNlaXZlZGF0aHFfLGRhdGVvZmluaXRpYWxkcnlpbmdpZm5lY2Vzc2FyeSwKCQlzYW1wbGVjb2xsZWN0ZWRpbmZpZWxkeW8sIGZpZWxkX2Rlcywgc2FtcGxld2V0b3JkcnkpKSAlPiUKCXJlbmFtZSgKCWZlbWFsZSA9IHNleCwKCWFnZSA9IGFnZV9jdWx0aXZhdGV1ciwKCW93biA9IGRfb3duLAoJY2xpZW50ID0gZF9jbGllbnQpICU+JQoJbXV0YXRlKAoJZmVtYWxlPSBpZmVsc2UoZmVtYWxlPT0iZ29yZSIsIDEsMCksCglmaWVsZC5zaXplID0gZmllbGRfZGltMSpmaWVsZF9kaW0yCgkpCgpkJHRvdGFsLnNlYXNvbnMgPC0gYXBwbHkoZFssIGdyZXAoImRfc2Vhc29uX2xpc3QiLCBuYW1lcyhkKSldLCAxLCBmdW5jdGlvbih4KSB7CglzdW0oeCwgbmEucm09VCl9KQpgYGAKCkNsZWFuIGFncm9ub21pYyBwcmFjdGljZSB2YXJpYWJsZXMKCmBgYHtyfQphZ1ZhcnMgPC0gYygibl9zZWFzb25fZmVydCIsICJuX3NlYXNvbl9jb21wb3N0IiwgIm5fc2Vhc29uX2xpbWUiLCAibl9zZWFzb25fZmFsbG93IiwKICAgICAgICAgICAgIm5fc2Vhc29uc19sZWdfMSIsICJuX3NlYXNvbnNfbGVnXzIiKQpzdW1tYXJ5KGRbLGFnVmFyc10pCmBgYAoKU29ydCBvdXQgdGhlIGxlZ3VtZXMgYXMgYSBzZWNvbmQgY3JvcAoKYGBge3J9CnRhYmxlKGQkbl9zZWFzb25zX2xlZ18yLCB1c2VOQSA9ICdpZmFueScpCmQkbl9zZWFzb25zX2xlZ18yIDwtIGlmZWxzZShkJG5fc2Vhc29uc19sZWdfMiA8MCB8IGQkbl9zZWFzb25zX2xlZ18yPjEwLCBOQSwgZCRuX3NlYXNvbnNfbGVnXzIpCmBgYAoKIyMjIENoZWNrIG91dCBjbGllbnQgdmFyaWFibGUgLSB3aHkgYXJlIHRoZXJlIG1pc3NpbmcgdmFsdWVzPwoKYGBge3J9CnRhYmxlKGQkY2xpZW50LCB1c2VOQSA9ICdpZmFueScpCmRbaXMubmEoZCRjbGllbnQpLCBjKCJzYW1wbGVfaWQiKV0KCiMgcmVwbGFjZSBjbGllbnQgYmFzZWQgb24gd2hldGhlciB0aGVyZSBpcyBhIEMgaW4gdGhlIGNsaWVudCB2YXJpYWJsZS4KZCRjbGllbnQuY2hlY2sgPC0gaWZlbHNlKGdyZXBsKCJDIiwgZCRzYW1wbGVfaWQpPT1ULCAwLCAxKQp0YWJsZShkJGNsaWVudCwgZCRjbGllbnQuY2hlY2spCmBgYAoKSXQgbG9va3MgbGlrZSBtb3N0IGZhcm1lcnMgd2VyZSByZWNvcmRlZCBjb3JyZWN0bHkgZXhjZXB0IGZvciBvbmUgZmFybWVyIHdobyB3YXMgY29kZWQgYXMgVHVidXJhIGZhcm1lciBidXQgdGhlaXIgc2FtcGxlX2lkIGluZGljYXRlIHRoZWlyIGEgY29udHJvbC4gTGV0J3MgdGFrZSBhIGxvb2s6CgpgYGB7cn0KZFtkJGNsaWVudD09MSAmIGQkY2xpZW50LmNoZWNrPT0wICYgIWlzLm5hKGQkZF9ncHMpLCBjKCJzYW1wbGVfaWQiLCAidHVidXJhY29udHJvbHRjIildCiMgdGhleSBzaG91bGQgYmUgYSBjb250cm9sCmRbZCRjbGllbnQ9PTEgJiBkJGNsaWVudC5jaGVjaz09MCAmICFpcy5uYShkJGRfZ3BzKSwgImNsaWVudCJdIDwtIDAKCiMgcmVtb3ZlIGZhcm1lcnMgZm9yIHdoaWNoIHdlIGhhdmUgc29pbCBkYXRhIGJ1dCBubyBzdXJ2ZXkgZGF0YSAodXNpbmcpCmQgPC0gZFstd2hpY2goZ3JlcGwoInVzaW5nIiwgZCRYX21lcmdlKSksXQp0YWJsZShkJGNsaWVudCwgZCRjbGllbnQuY2hlY2ssIHVzZU5BID0gJ2lmYW55JykKCiMgdXBkYXRlIGNsaWVudCB0byBlcXVhbCBjbGllbnQgY2hlY2sKZCRjbGllbnQgPC0gZCRjbGllbnQuY2hlY2sKYGBgCgpGaXggc29tZSBtb3JlIHZhcmlhYmxlIG5hbWVzOgpgYGB7cn0KbmFtZXMoZClbbmFtZXMoZCk9PSJmaWVsZF9rZ19mZXJ0MV8xIl0gPC0gImZpZWxkX2tnX2ZlcnQxXzE1YiIKbmFtZXMoZClbbmFtZXMoZCk9PSJmaWVsZF9rZ19mZXJ0Ml8xIl0gPC0gImZpZWxkX2tnX2ZlcnQyXzE1YiIKbmFtZXMoZClbbmFtZXMoZCk9PSJmaWVsZF9rZ19jb21wb3N0Il0gPC0gImZpZWxkX2tnX2NvbXBvc3RfMTViIgpgYGAKClJlY29kZSB2YXJpYWJsZXMgdG8gbnVtZXJpYzoKYGBge3J9CiMgcmVjb2RlIHRvIG51bWVyaWMKdmFybGlzdCA8LSBjKCJjbGllbnQiLCAib3duIiwgImNyb3AxXzE1Yl9zZWVka2ciLCAiY3JvcDFfMTViX3lpZWxkIiwgImNyb3AxXzE1Yl95aWVsZF8iLAoJImNyb3AyXzE1Yl9zZWVka2ciLCAiY3JvcDJfMTViX3lpZWxkIiwgImNyb3AyXzE1Yl95aWVsZF8iLCAiZmllbGRfa2dfZmVydDFfMTViIiwKCSJmaWVsZF9rZ19mZXJ0Ml8xNWIiLCAiZmllbGRfa2dfY29tcG9zdF8xNWIiLCAiZF9saW1lXzE1YiIsICJrZ19saW1lXzE1YiIpCgojIGNoZWNrIHRoYXQgdGhlcmUgYXJlbid0IHZhbHVlcyBoaWRkZW4gaW4gdGhlIGNoYXJhY3RlciB2YXJpYWJsZXMKI2FwcGx5KGRbLHZhcmxpc3RdLCAyLCBmdW5jdGlvbih4KXt0YWJsZSh4LCB1c2VOQT0naWZhbnknKX0pCgojIHJlY29kZSBjaGFyYWN0ZXJzIHRvIG51bWVyaWNzCmRbLCB2YXJsaXN0XSA8LSBzYXBwbHkoZFssdmFybGlzdF0sIGFzLm51bWVyaWMpCmBgYAoKIyMjIFNvcnQgb3V0IGtncyBvZiBsaW1lIGFwcGxpZWQKYGBge3J9CnRhYmxlKGQka2dfbGltZV8xNWIsIHVzZU5BID0gJ2lmYW55JykKZCRrZ19saW1lXzE1YiA8LSBpZmVsc2UoYWJzKGQka2dfbGltZV8xNWIpPT04OCwgTkEsIGQka2dfbGltZV8xNWIpCmBgYAoKCmBgYHtyfQojIGRpdmlkZSBvdXQgR1BTIGNvb3JkaW5hdGVzCiMgaHR0cDovL3JmdW5jdGlvbi5jb20vYXJjaGl2ZXMvMTQ5OQoKIyByZXBsYWNlIHRoZSBibGFuayBncHNfcGljX2d1aWRlIHdpdGggaW5mbwpkIDwtIGNiaW5kKGQsIHN0cl9zcGxpdF9maXhlZChkJGdwc19waWNfZ3VpZCwgIiAiLCBuPTQpKQpuYW1lcyhkKVtuYW1lcyhkKT09IjEiIHxuYW1lcyhkKT09ICIyIiB8IG5hbWVzKGQpPT0gIjMiIHwgbmFtZXMoZCk9PSAiNCJdIDwtIGMoImxhdCIsICJsb24iLCAiYWx0IiwgInByZWNpc2lvbiIpCmRbLGMoImxhdCIsICJsb24iLCAiYWx0IiwgInByZWNpc2lvbiIpXSA8LSBzYXBwbHkoZFssYygibGF0IiwgImxvbiIsICJhbHQiLCAicHJlY2lzaW9uIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpe2FzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHgpKX0pCgpgYGAKCiMjIENsZWFuaW5nIHNvaWwgZGF0YQpDbGVhbmluZyBvZiBzb2lsIGRhdGE6IENvbWUgYmFjaywgY2hlY2sgYW5kIGNsZWFuIHRoZSBzb2lsIGRhdGEgYmVmb3JlIG91dHB1dHRpbmcgdG8gY2xlYW4gZGF0YSBzZXQuIFBsb3QgZWFjaCBvZiB0aGUgc29pbCB2YXJpYWJsZXMgdG8gbG9vayBmb3IgdW5yZWFsaXN0aWMgdmFsdWVzLgpgYGB7cn0KZGltKGRbaXMubmEoZCRtMy5DYSksXSkKZCA8LSBkWy13aGljaChpcy5uYShkJG0zLkNhKSksXQogICAgICAgCnN1bW1hcnkoZFssYygibTMuQ2EiLCAibTMuTWciLCAicEgiLCAiVG90YWwuTiIsICJUb3RhbC5DIiwgIkV4QWwiKV0pCmBgYAoKIyMjIFNvaWwgVHlwZQoKYGBge3J9Cm5hbWVzKGQpW25hbWVzKGQpPT0iZ2VuZXJhbF9maWVsZF9pbmZvc29pbF90eXBlIl0gPC0gInNvaWxfdHlwZSIKbmFtZXMoZClbbmFtZXMoZCk9PSJnZW5lcmFsX2ZpZWxkX2luZm90ZXh0dXJlX3NvaWwiXSA8LSAic29pbF90ZXh0dXJlIgoKZCRibGFja19zb2lsIDwtIGlmZWxzZShkJHNvaWxfdHlwZT09ImJsYWNrIiwgMSwwKQpkJHJlZF9zb2lsIDwtIGlmZWxzZShkJHNvaWxfdHlwZT09InJlZCIsIDEsMCkKZCRzYW5keV9zb2lsIDwtaWZlbHNlKGQkc29pbF90eXBlPT0ic2FuZHkiLCAxLDApCmBgYAoKCkxldCdzIGNoZWNrIG91dCB0aGUgcm93cyBmb3Igd2hpY2ggd2UgZG9uJ3QgaGF2ZSBzb2lsIGRhdGEgYW5kIGRyb3AgdGhlbSBhcyB0aGV5IHdvbid0IGNvbnRyaWJ1dGUgdG8gdGhlIGZ1bGwgcGljdHVyZS4KCiMjIyBHcmFwaHMgb2YgUlcgYmFzZWxpbmUgc29pbCB2YXJpYWJsZXMKYGBge3J9CnNvaWxWYXJzIDwtIGMoIm0zLkNhIiwgIm0zLk1nIiwgInBIIiwgIlRvdGFsLk4iLCAiVG90YWwuQyIsICJFeEFsIikKYGBgCgpgYGB7cn0KZm9yKGkgaW4gMTpsZW5ndGgoc29pbFZhcnMpKXsKcHJpbnQoCiAgZ2dwbG90KGRhdGE9ZCwgYWVzKHg9YXMuZmFjdG9yKGNsaWVudCksIHk9ZFssc29pbFZhcnNbaV1dKSkgKyAKICAgIGdlb21fYm94cGxvdCgpICsKICAgIGxhYnMoeD0iVHVidXJhIEZhcm1lciIsIHk9c29pbFZhcnNbaV0sIHRpdGxlID0gcGFzdGUoIlJXIGJhc2VsaW5lIHNvaWwgLSAiLCBzb2lsVmFyc1tpXSwgc2VwID0gIiIpKQogICkgIAp9CgoKYGBgCgojIyMgQ2hlY2sgb3V0IHNvaWwgcmVsYXRpb25zaGlwcwpUaGVyZSBhcmUgYmlvbG9naWNhbGx5IHByZWRpY3RhYmxlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzb2lsIGNoZW1pY2FsIGNoYXJhY3RlcmlzdGljcy4gRm9yIGluc3RhbmNlLCB3ZSBleHBlY3QgQ2EgYW5kIE1nIHRvIG1vdmUgaW4gdGhlIHNhbWUgZGlyZWN0aW9uIGFuZCBiZSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBwSC4gSWYgd2UgaGFkIEFsdW1pbnVtIGFzIGFuIG91dGNvbWUsIHdlJ2QgZXhwZWN0IHBIIHRvIGJlIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHNvbHVhYmxlIGFsdW1pbnVtLiBMZXQncyBsb29rIHF1aWNrbHkgdG8gY29uZmlybSBpZiB0aG9zZSByZWxhdGlvbnNoaXBzIGFyZSBwcmVzZW50OgoKYGBge3J9CmdncGxvdChkLCBhZXMoeD1tMy5DYSwgeT1tMy5NZykpICsgZ2VvbV9wb2ludCgpICsKICAgIHN0YXRfc21vb3RoKG1ldGhvZD0ibG9lc3MiKSArCiAgICBsYWJzKHggPSAiQ2FsY2l1bSAobTMpIiwgeT0gIk1hZ25lc2l1bSAobTMpIiwgdGl0bGU9IkNhbGNpdW0gYW5kIE1hZ25lc2l1bSByZWxhdGlvbnNoaXAiKQoKZ2dwbG90KGQsIGFlcyh4PXBILCB5PW0zLkNhKSkgKyBnZW9tX3BvaW50KCkgKwogIHN0YXRfc21vb3RoKG1ldGhvZD0ibG9lc3MiKSArCiAgbGFicyh4ID0gInBIIiwgeT0iQ2FsY2l1bSAobTMpIiwgdGl0bGUgPSAicEggYW5kIENhbGNpdW0gcmVsYXRpb25zaGlwIikKCmdncGxvdChkLCBhZXMoeD1wSCwgeT1tMy5NZykpICsgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChtZXRob2Q9ImxvZXNzIikgKwogIGxhYnMoeCA9ICJwSCIsIHk9Ik1hZ25lc2l1bSAobTMpIiwgdGl0bGUgPSAicEggYW5kIE1hZ25lc2l1bSByZWxhdGlvbnNoaXAiKQoKZ2dwbG90KGQsIGFlcyh4PXBILCB5PUV4QWwpKSArIGdlb21fcG9pbnQoKSArCiAgc3RhdF9zbW9vdGgobWV0aG9kPSJsb2VzcyIpICsKICBsYWJzKHggPSAicEgiLCB5PSJFeGNoYW5nZWFibGUgQWx1bWludW0iLCB0aXRsZSA9ICJwSCBhbmQgQWx1bWludW0gcmVsYXRpb25zaGlwIikKCmdncGxvdChkLCBhZXMoeD1Ub3RhbC5DLCB5PVRvdGFsLk4pKSArIGdlb21fcG9pbnQoKSArIAogIHN0YXRfc21vb3RoKG1ldGhvZD0ibG9lc3MiKSArCiAgbGFicyh4ID0gIlRvdGFsIENhcmJvbiIsIHk9IlRvdGFsIE5pdHJvZ2VuIiwgdGl0bGUgPSAiQ2FyYm9uIGFuZCBOaXRyb2dlbiByZWxhdGlvbnNoaXAiKQpgYGAKClRoZSBzb2lsIGNoYXJhY3RlcmlzdGljcyBhcmUgbW92aW5nIGluIHRoZSBtYW5uZXIgdGhhdCBpcyBjb25zaXN0ZW50IHdpdGggb3VyIHVuZGVyc3RhbmRpbmcgb2Ygc29pbCBjaGVtaWNhbCBwcm9jZXNzZXMuCgpTYXZlIGNsZWFuIGRlbW9ncmFwaGljIGFuZCBzb2lsIGRhdGEgdG8gZXh0ZXJuYWwgZmlsZQpgYGB7cn0Kd3JpdGUuY3N2KGQsIGZpbGU9ImRhdGEvc2hzIHJ3IGJhc2VsaW5lLmNzdiIpCnNhdmUoZCwgZmlsZT0iZGF0YS9zaHMgcncgYmFzZWxpbmUuUmRhdGEiKQpgYGAKCgojIyMgTWFwIG9mIGJhc2VsaW5lIG9ic2VydmF0aW9ucwpQcm9kdWNlIGEgc2ltcGxlIG1hcCBvZiB3aGVyZSBvdXIgb2JzZXJ2YXRpb25zIGFyZQoKYGBge3IgZ2V0X21hcCwgaW5jbHVkZT1GfQppZiAoIShleGlzdHMoInJ3YW5kYSIpKSl7CiAgIyBPbmx5IG5lZWQgdG8gZ2VvY29kZSBvbmNlIHBlciBzZXNzaW9uIGxpYnJhcnkoZGlzbW8pCiAgcndhbmRhIDwtIHRyeShnZW9jb2RlKCJSd2FuZGEiKSkKICAjIElmIHRoZSBpbnRlcm5ldCBmYWlscywgdXNlIGEgbG9jYWwgdmFsdWUgCiAgaWYgKGNsYXNzKHJ3YW5kYSkgPT0gInRyeS1lcnJvciIpIHsKICAgIHJ3YW5kYSA8LSAiIgogICAgIyBhcnVzaGEkbG9uZ2l0dWRlIDwtIDM2LjY4Mjk5CiAgICAjIGFydXNoYSRsYXRpdHVkZSA8LSAtMy4zODY5MjUKICB9IAp9CmBgYAoKU2VlIFtoZXJlXShodHRwOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzIwODk5OF8zNTkyZDNjNmFjOWE0N2NjYmYzYTM5OTdlYzJiNjhlYy5odG1sKSBmb3IgbW9yZSBvbiB1c2luZyBtYXJrZXJDbHVzdGVyT3B0aW9ucyBpbiBsZWFmbGV0LgoKSW4gdGhlIG1hcCBiZWxvdywgdGhlIGxhcmdlciBncmVlbiBjaXJjbGVzIGFyZSBUdWJ1cmEgZmFybWVycyBhbmQgdGhlIHNtYWxsZXIgYmx1ZSBjaXJjbGVzIGFyZSBjb250cm9sIGZhcm1lcnMuCgpgYGB7ciBsZWFmbGV0LCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD03fQplIDwtIGRbIWlzLm5hKGQkbG9uKSxdCnNzIDwtIFNwYXRpYWxQb2ludHNEYXRhRnJhbWUoY29vcmRzID0gZVssIGMoImxvbiIsICJsYXQiKV0sIGRhdGE9ZSkKCnBhbCA8LSBjb2xvck51bWVyaWMoYygibmF2eSIsICJncmVlbiIpLCBkb21haW49dW5pcXVlKHNzJGNsaWVudCkpCm1hcCA8LSBsZWFmbGV0KCkgJT4lIGFkZFRpbGVzKCkgJT4lCiAgc2V0Vmlldyhsbmc9cndhbmRhJGxvbmdpdHVkZSwgbGF0PXJ3YW5kYSRsYXRpdHVkZSwgem9vbT04KSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKGxuZz1zcyRsb24sIGxhdD1zcyRsYXQsIAogICAgICAgICAgICAgICAgICAgcmFkaXVzPSBpZmVsc2Uoc3MkY2xpZW50PT0xLCAxMCw2KSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gcGFsKHNzJGNsaWVudCksCmNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoZGlzYWJsZUNsdXN0ZXJpbmdBdFpvb209MTMsIHNwaWRlcmZ5T25NYXhab29tPUZBTFNFKSkKCm1hcApgYGAKCgojIFN1bW1hcnkgc3RhdGlzdGljcwoKIyMjIFRhYmxlIG9mIGZpbmFsIGJhc2VsaW5lIGJyZWFrZG93bgpgYGB7cn0KY291bnQgPC0gZCAlPiUgZ3JvdXBfYnkoZGlzdHJpY3QpICU+JSAKICBkcGx5cjo6c3VtbWFyaXplKAogICAgdC5jb3VudCA9IHN1bShpZmVsc2UoY2xpZW50PT0xLDEsMCkpLAogICAgYy5jb3VudCA9IHN1bShpZmVsc2UoY2xpZW50PT0wLDEsMCkpLAogICAgdG90YWwgPSBuKCkKICApICU+JSB1bmdyb3VwKCkKCmNvdW50IDwtIGFzLmRhdGEuZnJhbWUoY291bnQpCndyaXRlLmNzdihjb3VudCwgZmlsZT0iZGF0YS9maW5hbCBydyBzYW1wbGUgYnJlYWtkb3duLmNzdiIsIHJvdy5uYW1lcz1GKQojYXMuZGF0YS5mcmFtZShjb3VudCkKYGBgCgojIyBCYXNlbGluZSBiYWxhbmNlCgpMZXQncyBzZWUgaG93IGJhbGFuY2VkIG91ciBmYXJtZXJzIGFyZSBpbiB0ZXJtcyBvZiBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMuIFR1YnVyYSBmYXJtZXJzIHdlcmUgc2VsZWN0ZWQgYmFzZWQgb24gKGxpc3QgY3JpdGVyaWEpIGFuZCBjb250cm9sIGZhcm1lcnMgaW4gdGhlIHNhbWUgYXJlYSB0aGEgZml0IHRoZSBzYW1lIGNyaXRlcmlhIHdlcmUgYWxzbyBzZWxlY3RlZC4gTm8gbWF0Y2hpbmcgcHJvY2VzcyBoYXMgYmVlbiBwZXJmb3JtZWQgdG8gaWRlbnRpZnkgdGhlIGNvbnRyb2wgZmFybWVycyB0aGF0IG1vc3QgY2xvc2VseSByZXNlbWJsZSB0aGUgVHVidXJhIGZhcm1lcnMgaW4gdGhlIHNhbXBsZS4gVGhlc2UgcmVzdWx0cyBhcmUgZW50aXJlbHkgcmVmbGVjdGluZyB0aGUgYmFsYW5jZSBpbmhlcmVudCBpbiB0aGUgaWRlbnRpZmljYXRpb24gcHJvY2Vzcywgbm90IGFueSBzdGF0aXN0aWNhbCBtYXRjaGluZyBvZiB0cmVhdG1lbnQgYW5kIGNvbnRyb2wuCgpgYGB7cn0KZCR2YWxsZXkgPC0gaWZlbHNlKGQkZ2VuZXJhbF9maWVsZF9pbmZvZmllbGRfbG9jYXRpb249PSJ2YWxsZXkiLCAxLDApCmQkaGlsbHNpZGUgPC0gaWZlbHNlKGQkZ2VuZXJhbF9maWVsZF9pbmZvZmllbGRfbG9jYXRpb249PSJoaWxsc2lkZSIsIDEsMCkKZCRoaWxsdG9wIDwtIGlmZWxzZShkJGdlbmVyYWxfZmllbGRfaW5mb2ZpZWxkX2xvY2F0aW9uPT0iaGlsbHRvcCIsIDEsMCkKbmFtZXMoZClbbmFtZXMoZCk9PSJnZW5lcmFsX2ZpZWxkX2luZm9ncmFkZV9oaWxsIl0gPC0gInNsb3BlIgpgYGAKCmBgYHtyfQpjb3IoZFssIGdyZXAoImJldGFpbF8iLCBuYW1lcyhkKSldLCB1c2U9J2NvbXBsZXRlLm9icycpCm5hbWVzKGQpW2dyZXAoImJldGFpbF8iLCBuYW1lcyhkKSldIDwtIGMoImNvd3MiLCAiZ29hdHMiLCAiY2hpY2tlbnMiLCAicGlncyIsICJzaGVlcCIpCgpkJG93bi5jb3dzIDwtIGlmZWxzZShkJGNvd3M+MCAmICFpcy5uYShkJGNvd3MpLCAxLDApCmQkb3duLmdvYXRzIDwtIGlmZWxzZShkJGdvYXRzPjAgJiAhaXMubmEoZCRnb2F0cyksIDEsMCkKZCRvd24uY2hpY2tlbnMgPC0gaWZlbHNlKGQkY2hpY2tlbnM+MCAmICFpcy5uYShkJGNoaWNrZW5zKSwgMSwwKQpkJG93bi5waWdzIDwtIGlmZWxzZShkJHBpZ3M+MCAmICFpcy5uYShkJHBpZ3MpLCAxLDApCmQkb3duLnNoZWVwIDwtIGlmZWxzZShkJHNoZWVwPjAgJiAhaXMubmEoZCRzaGVlcCksIDEsMCkKCgpgYGAKCiMjIENyb3Agc2VsZWN0aW9uIGluIDE1QgoKYGBge3J9Cm5hbWVzKGQpW25hbWVzKGQpPT0iaW5wdXR1c2VfMTViX3ByaW9yY3VsdHVyZV8xNWJfMSJdIDwtImNyb3BfMTViIgpkJGNyb3BfMTViIDwtIHRvbG93ZXIoZCRjcm9wXzE1YikKc29ydChwcm9wLnRhYmxlKHRhYmxlKGQkY3JvcF8xNWIsIHVzZU5BID0gJ2lmYW55JykpKQoKZCRjbGltYmluZ19iZWFuIDwtIGlmZWxzZShkJGNyb3BfMTViPT0ic2h5IiwgMSwwKQpkJHNvcmdodW0gPC0gaWZlbHNlKGQkY3JvcF8xNWI9PSJzYWthIiwgMSwwKQpkJGJ1c2hfYmVhbiA8LSBpZmVsc2UoZCRjcm9wXzE1Yj09ImJpZyIsIDEsMCkKZCRtYWl6ZSA8LSBpZmVsc2UoZCRjcm9wXzE1Yj09ImdvciIsIDEsMCkKYGBgCgpgYGB7cn0Kb3V0Lmxpc3QgPC0gYygiZmVtYWxlIiwgImFnZSIsICJoaHNpemUiLCAib3duIiwgImZpZWxkLnNpemUiLAogICAgICAgICAgICAgICJuX3NlYXNvbl9mZXJ0IiwgIm5fc2Vhc29uX2NvbXBvc3QiLCAibl9zZWFzb25fbGltZSIsICJuX3NlYXNvbl9mYWxsb3ciLCAibl9zZWFzb25zX2xlZ18xIiwgIm5fc2Vhc29uc19sZWdfMiIsICJzbG9wZSIsICAiYWx0IiwgInZhbGxleSIsICJoaWxsc2lkZSIsICJoaWxsdG9wIiwgImNvd3MiLCAiZ29hdHMiLCAiY2hpY2tlbnMiLCAicGlncyIsICJzaGVlcCIsICJjbGltYmluZ19iZWFuIiwgInNvcmdodW0iLCAiYnVzaF9iZWFuIiwibWFpemUiLCBzb2lsVmFycywgIm93bi5jb3dzIiwgIm93bi5nb2F0cyIsICJvd24uY2hpY2tlbnMiLCAib3duLnBpZ3MiLCAib3duLnNoZWVwIiwgImJsYWNrX3NvaWwiLCAicmVkX3NvaWwiLCAic2FuZHlfc29pbCIpCgpvdXRwdXQgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KG91dC5saXN0LCBmdW5jdGlvbih4KSB7CiAgCiAgb3V0IDwtIHQudGVzdChkWyx4XSB+IGRbLCJjbGllbnQiXSwgZGF0YT1kKQogIHRhYiA8LSBkYXRhLmZyYW1lKG91dFtbNV1dW1syXV0sb3V0W1s1XV1bWzFdXSwgb3V0WzNdKQogIHRhYlssMToyXSA8LSByb3VuZCh0YWJbLDE6Ml0sMykKICBuYW1lcyh0YWIpIDwtIGMobmFtZXMob3V0W1s1XV0pLCAicHZhbHVlIikKICByZXR1cm4odGFiKQp9KSkKCiMgdXNlIHAuYWRqdXN0IHdpdGggYm9uZmVycm9uaSBjb3JyZWN0aW9uCm91dHB1dCRwdmFsdWUgPC0gcC5hZGp1c3Qob3V0cHV0JHB2YWx1ZSwgbWV0aG9kPSJmZHIiKQpyb3duYW1lcyhvdXRwdXQpIDwtIG91dC5saXN0Cm91dHB1dCA8LSBvdXRwdXRbb3JkZXIob3V0cHV0JHB2YWx1ZSksXQpvdXRwdXQkcHZhbHVlIDwtIGlmZWxzZShvdXRwdXRbLCAzXSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKG91dHB1dFssIDNdLCAzKSkgCgoKY29sbmFtZXMob3V0cHV0KSA8LSBjKCJUdWJ1cmEgQ2xpZW50IiwiTm9uLVR1YnVyYSIsICJwLXZhbHVlIikJCmBgYAoKYGBge3IsIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZShvdXRwdXQpKQpgYGAKClByZS1QU00gdGFibGUgMSBzdW1tYXJ5CgpgYGB7cn0KdGFibGUxdmFycyA8LSBjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciKQp0YWJsZTEgPC0gb3V0cHV0W3Jvd25hbWVzKG91dHB1dCkgJWluJSB0YWJsZTF2YXJzLCBdCgp3cml0ZS5jc3YodGFibGUxLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCJydyB0YWJsZTEuY3N2Iiwgc2VwID0gIi8iKSwgcm93Lm5hbWVzPVQpCmBgYAoKCmBgYHtyfQojd3JpdGUgdGFibGUKd3JpdGUuY3N2KG91dHB1dCwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgImJhc2VsaW5lIGJhbGFuY2UuY3N2Iiwgc2VwPSIvIiksIHJvdy5uYW1lcz1UKQpgYGAKCmBgYHtyfQojIHdyaXRlIGJhc2VsaW5lIGJhbGFuY2UgdGFibGUgZm9yIEVTIHBlciBoaXMgdGFibGUgbGF5b3V0cwp0NG9yZGVyIDwtIGMoImFnZSIsICJmZW1hbGUiLCAiaGhzaXplIiwgIm93biIpCnRhYmxlNHZhcnMgPC0gcGFzdGUodDRvcmRlciwgY29sbGFwc2U9InwiKQoKcncudGFibGU0IDwtIG91dHB1dFtncmVwbCh0YWJsZTR2YXJzLCByb3duYW1lcyhvdXRwdXQpKSxdCnJ3LnRhYmxlNCA8LSBydy50YWJsZTRbb3JkZXIobWF0Y2gocm93bmFtZXMocncudGFibGU0KSwgdDRvcmRlcikpLF0Kd3JpdGUuY3N2KHJ3LnRhYmxlNCwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInByZSBtYXRjaCBiYWxhbmNlIHRhYmxlNC5jc3YiLCBzZXA9Ii8iKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IFQpCgp0NW9yZGVyIDwtIGMoImZpZWxkLnNpemUiLCAic2xvcGUiLCAiYWx0IiwgImhpbGx0b3AiLCAiaGlsbHNpZGUiLCAidmFsbGV5IiwKICAgICAgICAgICAgICAgICAgICAgICJjbGltYmluZ19iZWFuIiwgInNvcmdodW0iLCAiYnVzaF9iZWFuIiwgIm1haXplIikKCnRhYmxlNXZhcnMgPC0gcGFzdGUodDVvcmRlcixjb2xsYXBzZT0ifCIpCgpydy50YWJsZTUgPC0gb3V0cHV0W2dyZXBsKHRhYmxlNXZhcnMsIHJvd25hbWVzKG91dHB1dCkpLF0KcncudGFibGU1IDwtIHJ3LnRhYmxlNVtvcmRlcihtYXRjaChyb3duYW1lcyhydy50YWJsZTUpLCB0NW9yZGVyKSksXQp3cml0ZS5jc3YocncudGFibGU1LCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicHJlIG1hdGNoIGJhbGFuY2UgdGFibGU1LmNzdiIsIHNlcCA9ICIvIiksCiAgICAgICAgICByb3cubmFtZXMgPSBUKQoKdDZvcmRlciA8LSBjKCJvd24uY293cyIsICJjb3dzIiwgIm93bi5waWdzIiwgInBpZ3MiLCAib3duLnNoZWVwIiwgInNoZWVwIiwgIm93bi5nb2F0cyIsICJnb2F0cyIsIm93bi5jaGlja2VucyIsICJjaGlja2VucyIpCnRhYmxlNnZhcnMgPC0gcGFzdGUodDZvcmRlciwgY29sbGFwc2UgPSAifCIpCnJ3LnRhYmxlNiA8LSBvdXRwdXRbZ3JlcCh0YWJsZTZ2YXJzLCByb3duYW1lcyhvdXRwdXQpKSwgXQpydy50YWJsZTYgPC0gcncudGFibGU2W29yZGVyKG1hdGNoKHJvd25hbWVzKHJ3LnRhYmxlNiksIHQ2b3JkZXIpKSxdCndyaXRlLmNzdihydy50YWJsZTYsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJwcmUgbWF0Y2ggYmFsYW5jZSB0YWJsZTYuY3N2Iiwgc2VwID0gIi8iKSwgCiAgICAgICAgICByb3cubmFtZXM9VCkKYGBgCgoKIyMgT3ZlcmFsbCBiYWxhbmNlIGludGVycHJldGF0aW9uCioqRGVtb2dyYXBoaWMgdmFyaWFibGVzKiogV2UgYXJlIG5vdCB3ZWxsIGJhbGFuY2VkIGFsb25nIHRoZSBtYWluIGRlbW9ncmFwaGljIHZhcmlhYmxlcyB3ZSBjb2xsZWN0ZWQsIHNleCwgYWdlIGFuZCBISCBzaXplLiBGb3IgdGhlIHB1cnBvc2VzIG9mIGluZmVyZW5jZSB3ZSBjYW4gdGVzdCBzb21lIG1hdGNoaW5nIGFsZ29yaXRobXMgdG8gaW1wcm92ZSB0aGUgbWF0Y2ggYmV0d2VlbiBUdWJ1cmEgYW5kIGNvbnRyb2wgZmFybWVycy4gCgoKKipBZ3JpY3VsdHVyYWwgcHJhY3RpY2UgdmFyaWFibGVzKiogV2UgYXJlIGRlY2VudGx5IGJhbGFuY2VkIGFsb25nIGFncmljdWx0dXJhbCBwcmFjdGljZSB2YXJpYWJsZXMuIE91ciBjb3Vyc2Ugb2YgYWN0aW9uIGhlcmUgaXMgc2ltaWxpYXIgdG8gb3VyIG9wdGlvbnMgd2l0aCB0aGUgZGVtb2dyYXBoaWMgdmFyaWFibGVzLiAKCgoqKlNvaWwgVmFyaWFibGVzKiogV2UgYXJlIGJhbGFuY2VkIG9uIHRoZSBwcmltYXJ5IHNvaWwgdmFyaWFibGVzIG9mIGludGVyZXN0IGJldHdlbiBvdXIgVHVidXJhIGZhcm1lcnMgYW5kIHRoZSBjb21wYXJpc29uIGZhcm1lcnMuCgojIyBCYXNlbGluZSBiYWxhbmNlIGJ5IGRpc3RyaWN0CmBgYHtyfQpkaXN0Lm91dHB1dCA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoc3BsaXQoZCwgZCRkaXN0cmljdCksIGZ1bmN0aW9uKHgpIHsKICAKICB0YWIgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KG91dC5saXN0LCBmdW5jdGlvbih5KSB7CiAgICAKICAgIG91dCA8LSB0LnRlc3QoeFsseV0gfiB4WywiY2xpZW50Il0sIGRhdGE9eCkKICAgIHRhYiA8LSBkYXRhLmZyYW1lKG91dFtbNV1dW1syXV0sb3V0W1s1XV1bWzFdXSwgb3V0WzNdKQogICAgdGFiWywxOjJdIDwtIHJvdW5kKHRhYlssMToyXSwzKQogICAgbmFtZXModGFiKSA8LSBjKG5hbWVzKG91dFtbNV1dKSwgInB2YWx1ZSIpCiAgICAjdGFiWywzXSA8LSBwLmFkanVzdCh0YWJbLDNdLCBtZXRob2Q9ImhvbG0iKQogICAgI3RhYlssM10gPC0gaWZlbHNlKHRhYlssM10gPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0YWJbLDNdLDMpKQogICAgI3ByaW50KHRhYikKICAgIHJldHVybih0YWIpCiAgfSkpCiAgCiAgcmV0dXJuKGRhdGEuZnJhbWUoZGlzdHJpY3QgPSB1bmlxdWUoeCRkaXN0cmljdCksIHRhYikpCn0pKQoKcm93bmFtZXMoZGlzdC5vdXRwdXQpIDwtIE5VTEwKZGlzdC5vdXRwdXQkdmFyaWFibGUgPC0gcmVwKG91dC5saXN0LGxlbmd0aCh1bmlxdWUoZCRkaXN0cmljdCkpKQkKCiMgb3JkZXIgdmFyaWFibGVzIApkaXN0Lm91dHB1dCA8LSBkaXN0Lm91dHB1dFssIGMoMSwgNSwgMjo0KV0KZGlzdC5vdXRwdXQkcHZhbHVlIDwtIHAuYWRqdXN0KGRpc3Qub3V0cHV0JHB2YWx1ZSwgbWV0aG9kPSJmZHIiKQpkaXN0Lm91dHB1dCA8LSBkaXN0Lm91dHB1dFtvcmRlcihkaXN0Lm91dHB1dCRwdmFsdWUpLF0KCmRpc3Qub3V0cHV0JHB2YWx1ZSA8LSBpZmVsc2UoZGlzdC5vdXRwdXQkcHZhbHVlIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQoZGlzdC5vdXRwdXQkcHZhbHVlLDMpKQpjb2xuYW1lcyhkaXN0Lm91dHB1dCkgPC0gYygiRGlzdHJpY3QiLCAiVmFyaWJsZSIsICJUdWJ1cmEgQ2xpZW50IiwiTm9uLVR1YnVyYSIsICJwLXZhbHVlIikJCmBgYAoKYGBge3IsIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZShkaXN0Lm91dHB1dCkpCmBgYAoKIyMgRGlzdHJpY3QgYmFsYW5jZSBpbnRlcnByZXRhdGlvbgoqKkRlbW9ncmFwaGljIHZhcmlhYmxlcyoqIGludGVycHJldGF0aW9uIGhlcmUuCgoKKipBZ3JpY3VsdHVyYWwgcHJhY3RpY2UgdmFyaWFibGVzKiogaW50ZXJwcmV0YXRpb24gaGVyZQoKCioqU29pbCBWYXJpYWJsZXMqKiBpbnRlcnByZXRhdGlvbiBoZXJlCgoKYGBge3J9CndyaXRlLmNzdihkaXN0Lm91dHB1dCwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgImRpc3RyaWN0IGJhbGFuY2UuY3N2Iiwgc2VwPSIvIiksIHJvdy5uYW1lcz1UKQpgYGAKCiMjIEJhc2VsaW5lIGJhbGFuY2UgYnkgVHVidXJhIHRlbnVyZQpMb29rIGF0IGZhcm1lcnMgYnkgZHVyYXRpb24gb2YgdGVudXJlIGZhcm1pbmcgd2l0aCBUdWJ1cmEuIFdlIHdhbnQgdG8gdW5kZXJzdGFuZCwgYXQgbGVhc3Qgd2l0aCBhbiBpbml0aWFsIG5haXZlIGJhc2VsaW5lIHNlbnNlLCB3aGF0IGlzIHRoZSBjdW11bGF0aXZlIGVmZmVjdCBvZiBUdWJ1cmEgcHJhY3RpY2VzIG9uIHNvaWwgaGVhbHRoIG91dGNvbWVzPwoKV2Ugd2lsbCBsb29rIG9ubHkgYXQgY3VycmVudCBUdWJ1cmEgZmFybWVycyBhbmQgY29tcGFyZSBmaXJzdCB5ZWFyIGZhcm1lcnMgdG8gZmFybWVycyB3aXRoIG1vcmUgZXhwZXJpZW5jZSB3aXRoIFR1YnVyYS4KCmBgYHtyfQpvYWZPbmx5IDwtIGRbd2hpY2goZCRjbGllbnQ9PTEgJiBkJHRvdGFsLnNlYXNvbnM+PTEpLF0KCm5UZW51cmUgPC0gb2FmT25seSAlPiUgZ3JvdXBfYnkodG90YWwuc2Vhc29ucykgJT4lIAogIGRwbHlyOjpzdW1tYXJpemUoCiAgICBuID0gbigpCiAgKSAlPiUgdW5ncm91cCgpICU+JSBhcy5kYXRhLmZyYW1lKCkKblRlbnVyZSR2YWwgPC0gcGFzdGUoblRlbnVyZSR0b3RhbC5zZWFzb25zLCAiICgiLCAibiA9ICIsIG5UZW51cmUkbiwgIikiLCBzZXAgPSAiIikKCmZvcihpIGluIDE6bGVuZ3RoKHNvaWxWYXJzKSl7CnByaW50KAogIGdncGxvdChvYWZPbmx5LCBhZXMoeD1hcy5mYWN0b3IodG90YWwuc2Vhc29ucyksIHk9b2FmT25seVssc29pbFZhcnNbaV1dKSkgKyAKICAgIGdlb21fYm94cGxvdCgpICsKICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPW5UZW51cmUkdmFsKSArIAogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICAgIGxhYnMoeD0iVHVidXJhIFRlbnVyZSIsIHk9c29pbFZhcnNbaV0sIHRpdGxlID0gcGFzdGUoIlJXIGJhc2VsaW5lIHNvaWwgYnkgdGVudXJlIC0gIiwgc29pbFZhcnNbaV0sIHNlcCA9ICIiKSkKICApICAKfQpgYGAKCiMjIFRlbnVyZSBzdW1tYXJpZXMKYGBge3J9CnRlbnVyZVN1bSA8LSBhZ2dyZWdhdGUob2FmT25seVssb3V0Lmxpc3RdLCBieT1saXN0KG9hZk9ubHkkdG90YWwuc2Vhc29ucyksIGZ1bmN0aW9uKHgpewogIHJvdW5kKG1lYW4oeCwgbmEucm09VCksMikKfSkKdGVudXJlU3VtIDwtIGFzLmRhdGEuZnJhbWUodCh0ZW51cmVTdW0pKQpjb2xuYW1lcyh0ZW51cmVTdW0pIDwtIGMocGFzdGUoc2VxKDEsMTQsMSksICIgc2Vhcy4iLCBzZXAgPSAiIikpCmBgYAoKYGBge3IsIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZSh0ZW51cmVTdW0pKQpgYGAKCiMjIFRlbnVyZSBiYWxhbmNlCldlJ3JlIGRlZmluaW5nIFR1YnVyYSB0ZW51cmUgYXMgaGF2aW5nIDMgb3IgbW9yZSBzZWFzb25zIG9mIGV4cGVyaWVuY2UgZmFybWluZyB3aXRoIFR1YnVyYS4gV2UgZHJhdyB0aGUgbGluZSBhdCAzIHNlYXNvbnMgYXMgdGhyZWUgc2Vhc29ucyBvZiBmZXJ0aWxpemVyIHVzZSBpcyBhcHByb3hpbWF0ZWx5IHdoZW4gd2UnZCBleHBlY3QgZmVydGlsaXplciB0byBzdGFydCB0byBoYXZlIGEgZGV0cmltZW50YWwgZWZmZWN0IG9uIHNvaWwgaGVhbHRoLgoKYGBge3J9Cm9hZk9ubHkkdGVudXJlZCA8LSBpZmVsc2Uob2FmT25seSR0b3RhbC5zZWFzb25zPj0zLDEsMCkKCnRlbnVyZSA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkob3V0Lmxpc3QsIGZ1bmN0aW9uKHgpIHsKICAKICBvdXQgPC0gdC50ZXN0KG9hZk9ubHlbLHhdIH4gb2FmT25seVssInRlbnVyZWQiXSwgZGF0YT1vYWZPbmx5KQogIHRhYiA8LSBkYXRhLmZyYW1lKG91dFtbNV1dW1sxXV0sIG91dFtbNV1dW1syXV0sIG91dFszXSkKICB0YWJbLDE6Ml0gPC0gcm91bmQodGFiWywxOjJdLDMpCiAgbmFtZXModGFiKSA8LSBjKG5hbWVzKG91dFtbNV1dKSwgInB2YWx1ZSIpCiAgcmV0dXJuKHRhYikKfSkpCgojIHVzZSBwLmFkanVzdCB3aXRoIGJvbmZlcnJvbmkgY29ycmVjdGlvbgp0ZW51cmUkcHZhbHVlIDwtIHAuYWRqdXN0KHRlbnVyZSRwdmFsdWUsIG1ldGhvZD0iZmRyIikKcm93bmFtZXModGVudXJlKSA8LSBvdXQubGlzdAp0ZW51cmUgPC0gdGVudXJlW29yZGVyKHRlbnVyZSRwdmFsdWUpLF0KdGVudXJlJHB2YWx1ZSA8LSBpZmVsc2UodGVudXJlWywgM10gPCAwLjAwMSwgIjwgMC4wMDEiLCByb3VuZCh0ZW51cmVbLCAzXSwgMykpIAoKCmNvbG5hbWVzKHRlbnVyZSkgPC0gYygiTm9uLVR1YnVyYSIsICJUdWJ1cmEgQ2xpZW50IiwgInAtdmFsdWUiKQkKYGBgCgpgYGB7ciwgcmVzdWx0cz0nYXNpcyd9CnByaW50KGthYmxlKHRlbnVyZSkpCmBgYAoKIyMgVGVudXJlZCB2LiBuZXcgYmFsYW5jZSBpbnRlcnByZXRhdGlvbgoKKipEZW1vZ3JhcGhpYyB2YXJpYWJsZXMqKiBXZSBhcmUgd2VsbCBiYWxhbmNlZCBhbG9uZyBkZW1vZ3JhcGhpYyB2YXJpYWJsZXMuCgoqKkFncmljdWx0dXJhbCBwcmFjdGljZSB2YXJpYWJsZXMqKiBOb3Qgc3VycHJpc2luZ2x5LCBUdWJ1cmEgZmFybWVycyBoYXZlIG1vcmUgY3VtdWxhdGl2ZSB5ZWFycyBvZiBmZXJ0aWxpemVyIHVzZSB0aGFuIGN1cnJlbnQgbm9uLVR1YnVyYSBmYXJtZXJzLiBXaGlsZSB0aGF0IGRpZmZlcmVuY2UgaXMgc2lnbmZpY2FudCwgaXQgaXMgcmVhbGlzdGljYWxseSBvbmx5IGEgc2luZ2xlIHNlYXNvbiBvZiBmZXJ0aWxpemVyIHVzZSBkaWZmZXJlbnQuCgpJbnRlcmVzdGluZ2x5LCBub24tVHVidXJhIGZhcm1lcnMgcmVwb3J0ZWQgdXNpbmcgbW9yZSBsaW1lIHRoYW4gY3VycmVudCBUdWJ1cmEgZmFybWVycy4gVGhpcyAKCioqU29pbCBWYXJpYWJsZXMqKiBTb2lsIHBILCBjYWxjaXVtIGFuZCBtYWduZXNpdW0gbGV2ZWxzIGFyZSBsb3dlciBmb3IgdGVudXJlZCBUdWJ1cmEgZmFybWVycy4gVGhpcyBpcyBjb25zaXN0ZW50IHdpdGggdGhlIGh5cG90aGVzaXMgdGhhdCBpbmNyZWFlc2QgZmVydGlsaXplciB1c2UgbGVhZHMgdG8gYW4gaW5jcmVhZXNlIGluIHNvaWwgYWNpZGl0eS4KCiMjIEFuYWx5c2lzIG9mIGFncm9ub21pYyBwcmFjdGljZXMgY29udHJpYnV0aW5nIHRvIHNvaWwgaGVhbHRoIG91dGNvbWVzCgpIZXJlJ3Mgd2hlcmUgd2UnbGwgbG9vayBhdCB0aGUgY29udHJpYnV0aW9uIG9mIGZlcnRpbGl6ZXIsIGxpbWUgYW5kIGN1bHRpdmF0aW9uIHByYWN0aWNlcyBvbiBzb2lsIGhlYWx0aCBvdXRjb21lcy4gVGhpcyBhbmFseXNpcyB3aWxsIGJlIGNvbWUgcmljaGVyIGFzIHdlIGdhaW4gbG9uZ2l0dWRpbmFsIG1lYXN1cmVzLiBJIGNhdXRpb24gdGhhdCB3ZSBjYW5ub3QgdHJlYXQgdGhlc2UgcmVsYXRpb25zaGlwcyBhcyBjYXVzYWwuIFRoZSBkaXJlY3Rpb24gb2YgY2F1c2FsaXR5IGlzIG5vdCBjbGVhcmx5IGRlbGluZWF0ZWQgaW4gdGhlIGRhdGEgb3IgdGhlIHN0dWR5IGRlc2lnbi4gSG93ZXZlciwgd2UgY2FuIGlkZW50aWZ5IG1lYW5pbmdmdWwgY29ubmVjdGlvbnMgYmV0d2VlbiBwcmFjdGljZXMgYW5kIG91dGNvbWVzIHRocm91Z2ggdGhpcyBhbmFseXNpcyB0byBnZW5lcmF0ZSBuZXcgaHlwb3RoZXNlcyBmb3IgZmllbGQgdGVzdGluZy4KCkknbSBnb2luZyB0byBzdGFydCB3aXRoIGJlaGF2aW9ycyBieSBzZWN0aW9ucyBhbmQgdGhlbiBtb3ZlIHRvIGEgbW9yZSBjb21wcmVoZW5zaXZlIG1vZGVsIGluY2x1ZGluZyBtdWx0aXBsZSBwcmFjdGljZXMuIEFsbCBtb2RlbHMgd2lsbCBpbmNsdWRlIGNvbnRyb2xzIGZvciBzaXRlIHRvIGFjY291bnQgZm9yIGxvY2FsIHZhcmlhdGlvbiBhbmQgZmllbGQgb2ZmaWNlciBiZWhhdmlvci4KCiMgQW5hbHlzaXMKCiMjIEFncm9ub21pYyBCZWhhdmlvcnMKCioqQ2hlY2sqKiBmb3IgbXVsdGljb2xsaW5lYXJpdHkgYmVmb3JlIGFkZGluZyBudW1iZXIgb2Ygc2Vhc29ucyBvZiBhZ3Jvbm9taWMgaW5wdXRzIG9uIHRoZSBzYW1lIHNpZGUgb2YgdGhlIHJlZ3Jlc3Npb24uICAKCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoc3RhcmdhemVyKSkKCmlucHV0VXNlIDwtIGMoIm5fc2Vhc29uX2ZlcnQiLCJuX3NlYXNvbl9jb21wb3N0IiwgIm5fc2Vhc29uX2xpbWUiLCAibl9zZWFzb25fZmFsbG93IikKCmNvcihkWyxpbnB1dFVzZV0sIHVzZT0iY29tcGxldGUub2JzIikKYGBgCgoqKkludGVycHJldGF0aW9uKio6IFRoZSBzdHJvbmdlc3QgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgaW5wdXQgdXNlIGludGVuc2l0eSB2YXJpYWJsZXMgaXMgYmV0d2VlbiBzZWFzb25zIG9mIGZlcnRpbGl6ZXIgYW5kIGNvbXBvc3QgdXNlLCB+MC4zNS4gV2hpbGUgdGhpcyBpcyBvbiB0aGUgaGlnaGVyIGVuZCBpdCdzIG5vdCBuZWNlc3NhcmlseSBjYXVzZSBmb3IgYWxhcm0uCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KaW5wdXRVc2UgPC0gcGFzdGUoYygibl9zZWFzb25fZmVydCIsIm5fc2Vhc29uX2NvbXBvc3QiLCAibl9zZWFzb25fbGltZSIsICJuX3NlYXNvbl9mYWxsb3ciKSwgY29sbGFwc2U9ICIgKyAiKQoKbGlzdDEgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsIGlucHV0VXNlLCAiKyBhcy5mYWN0b3IoY2VsbCkiLCBzZXA9IiIpKSwgZGF0YT1kKQogIHJldHVybihtb2QpCn0pCgpzdGFyZ2F6ZXIobGlzdDEsIHR5cGU9Imh0bWwiLCAKICAgICAgICAgIHRpdGxlID0gIjIwMTZBIFJ3YW5kYSBTb2lsIEhlYWx0aCBCYXNlbGluZSAtIE5haXZlIEFncm9ub21pYyBQcmFjdGljZSBNb2RlbHMiLAogICAgICAgICAgY292YXJpYXRlLmxhYmVscyA9IGMoIlNlYXNvbnMgb2YgRmVydGlsaXplciIsICJTZWFzb25zIG9mIENvbXBvc3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZWFzb25zIG9mIExpbWUiLCAiU2Vhc29ucyBvZiBGYWxsb3ciKSwKICAgICAgICAgIGRlcC52YXIuY2FwdGlvbiA9ICIiLAogICAgICAgICAgZGVwLnZhci5sYWJlbHMgPSAiIiwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKGdzdWIoIm0zLiIsIiIsIHNvaWxWYXJzKSksCiAgICAgICAgICBub3RlcyA9ICJJbmNsdWRlcyBGRSBmb3IgY2VsbCIsCiAgICAgICAgICBvbWl0PWMoImNlbGwiKSwgb3V0PXBhc3RlKCJvdXRwdXQiLCAicndfYmFzZWxpbmVfYWdwcmFjLmh0bSIsIHNlcD0iLyIpKQpgYGAKCgoqKkludGVycHJldGF0aW9uKiogVGhlIG5haXZlIG1vZGVsIHN1Z2dlc3RzIHRoYXQgd2hlbiB3ZSBpbmNsdWRlIHNpdGUgbGV2ZWwgZml4ZWQgZWZmZWN0cywgZHVyYXRpb24gb2YgYWdyb25vbWljIHByYWN0aWNlcyBkb24ndCBoYXZlIGEgYmlnIGVmZmVjdCBvbiBzb2lsIGhlYWx0aCBvdXRjb21lcy4gSG93ZXZlciwgc29tZSBvZiB0aGUgcHJhY3RpY2UgaW50ZW5zaXR5IHZhcmlhYmxlcyBhcmUgbm90IHdlbGwgZGlzdHJpYnV0ZWQuIExldCdzIHRha2UgYSBsb29rIGF0IGEgbG9nIHRyYW5zZm9ybWF0aW9uLiBJJ20gYWRkaW5nIG9uZSB0byB0aGUgdmFyaWFibGVzIGFzIHRvIG5vdCBlbmQgdXAgd2l0aCBsb3RzIG9mIEluZiB2YWx1ZXMuCgpMb2cgdHJhbnNmb3JtYXRpb25zIGluIHRoZW9yeSBhcmUgYXBwcm9wcmlhdGUgZm9yIHZhcmlhYmxlcyB0aGF0IGFyZSByaWdodCBza2V3ZWQgKHZhdmx1ZXMgY2x1c3RlcmVkIHRvIHRoZSBsZWZ0IG9mIHRoZSBkaXN0cmlidXRpb24pIGFuZCBzZWUgZGltaW5pc2hpbmcgcmV0dXJucyB0byBpbmNyZWFzaW5nIHZhbHVlcy4gVGhlIHNoYXBlIG9mIHRoZSBkYXRhIHN1Z2dlc3RzIGEgbG9nIHRyYW5zZm9ybWF0aW9uIGJ1dCBpdCdzIGRlYmF0ZWFibGUgd2hldGhlciB0aGUgcmVsYXRpb25zaGlwIGlzIGRpbWluaXNoaW5nLgoKYGBge3J9CmFnUHJhYyA8LSBjKG5hbWVzKGRbZ3JlcCgnbl9zZWFzb25fJywgbmFtZXMoZCkpXSkpCgpmb3IoaSBpbiAxOmxlbmd0aChhZ1ByYWMpKXsKICBwcmludCgKICAgIGdncGxvdChkLCBhZXMoeD1kWyxhZ1ByYWNbaV1dKSkgKyBnZW9tX2RlbnNpdHkoKSArCiAgICAgIGxhYnMoeCA9IHBhc3RlKGFnUHJhY1tpXSwgIiBObyB0cmFuc2Zvcm0iLCBzZXAgPSAiIikpCiAgICAgICAgKQp9CgojIHNpbmNlIHRoZXNlIGFyZSBhbGwgc2tld2VkLCBjb25zaWRlciBhIGxvZyB0cmFuc2Zvcm0gCmZvcihpIGluIDE6bGVuZ3RoKGFnUHJhYykpewogIHByaW50KAogICAgZ2dwbG90KGQsIGFlcyh4PWxvZzEwKGRbLGFnUHJhY1tpXV0rMSkpKSArIGdlb21fZGVuc2l0eSgpICsgCiAgICAgIGxhYnMoeCA9IHBhc3RlKGFnUHJhY1tpXSwgIiBMb2cgdHJhbnNmb3JtIiwgc2VwID0gIiIpKQogICAgICAgICkKfQoKIyBsb29rIGF0IG90aGVyIHRyYW5zZm9tYXRpb25zCmZvcihpIGluIDE6bGVuZ3RoKGFnUHJhYykpewogIHByaW50KAogICAgZ2dwbG90KGQsIGFlcyh4PWRbLGFnUHJhY1tpXV1eKDEvMykpKSArIGdlb21fZGVuc2l0eSgpICsKICAgICAgbGFicyh4ID0gcGFzdGUoYWdQcmFjW2ldLCAiIGN1YmljIHJvb3QgdHJhbnNmb3JtIiwgc2VwID0gIiIpKQogICAgICAgICkKfQoKCiMgdmlzdWFsaXplIHRoZSBvdXRjb21lcyBhcyB3ZWxsIHRvIHNlZSBpZiBhIHRyYW5zZm9ybWF0aW9uIGlzIHdhcnJhbnRlZApmb3IoaSBpbiAxOmxlbmd0aChzb2lsVmFycykpewogIHByaW50KAogICAgZ2dwbG90KGQsIGFlcyh4PWRbLHNvaWxWYXJzW2ldXSkpICsgZ2VvbV9kZW5zaXR5KCkgKwogICAgICBsYWJzKHggPSBzb2lsVmFyc1tpXSwgdGl0bGUgPSBzb2lsVmFyc1tpXSkKICAgICAgICApCn0KYGBgCgpgYGB7cn0KZCRsb2dGZXJ0IDwtIGxvZyhkJG5fc2Vhc29uX2ZlcnQrMSkKZCRsb2dDb21wb3N0IDwtIGxvZyhkJG5fc2Vhc29uX2NvbXBvc3QrMSkKZCRsb2dMaW1lIDwtIGxvZyhkJG5fc2Vhc29uX2xpbWUrMSkKZCRsb2dGYWxsb3cgPC0gbG9nKGQkbl9zZWFzb25fZmFsbG93KzEpCmBgYAoKT3IgbG9vayBhdCBCb3hDb3ggZ3JhcGggdG8gZW1waXJpY2FsbHkgZGV0ZXJtaW5lIHRoZSByaWdodCB0cmFuc2Zvcm1hdGlvbi4gTG9nIGlzIGFzc3VtaW5nIGEgZGltaW5pc2hpbmcgcmV0dXJuIHRvIGFuIGluY3JlYXNpbmcgWC4gVGhhdCdzIHByb2JhYmx5IG5vdCB0aGUgY2FzZSB3aXRoIGZlcnRpbGl6ZXIuIFdlJ2QgYWN0dWFsbHkgZXhwZWN0IGFuIGluY3JlYXNpbmcgcmV0dXJuIGFzIHZhbHVlcyBnZXQgbGFyZ2VyLiBXZSB1c2UgYm94Y294IHRvIHNlZSB3aGF0IHRoZSBkYXRhIHN1Z2dlc3QuIFdlIGludGVycHJldCBpdCBhcyBmb2xsb3dzOgoKKiBsYW1iZGEgPSAyIC0+IHNxdWFyZQoqIGxhbWJkYSA9IDEgLT4gbm8gdHJhbnNmb3JtYXRpb24KKiBsYW1iZGEgPSAwLjUgLT4gc3F1YXJlIHJvb3QKKiBsYW1iZGEgPSAwIC0+IGxvZwoqIGxhbWRiYSA9IC0xIC0+IGludmVyc2UKCmBgYHtyfQpsaWJyYXJ5KE1BU1MpCmZvcihpIGluIDE6bGVuZ3RoKGFnUHJhYykpewpib3hjb3gobG0ocEggfiBkWyxhZ1ByYWNbaV1dLCBkYXRhPWQpKQp9CmBgYAoKRm9yIHBIIGF0IGxlYXN0IGl0IHNlZW1zIGxpa2UgYSBsb2cgdHJhbnNmb3JtIGlzIGFwcHJvcHJpYXRlLiBXZSBjYW4gcnVuIHRoaXMgZm9yIGFsbCBvdGhlciB2YXJpYWJsZXMgYXMgd2VsbCB0byBzZWUgd2hhdCB3ZSBnZXQgYXMgd2VsbC4KCkxldCdzIGxvb2sgYXQgdGhlIGxvZyByZXN1bHRzOiBTZWUgW2hlcmVdKGh0dHA6Ly93d3cuY2F6YWFyLmNvbS90YS9lY29uMTEzL2ludGVycHJldGluZy1iZXRhKSBhbmQgW2hlcmVdKGh0dHA6Ly93d3cua2VuYmVub2l0Lm5ldC9jb3Vyc2VzL01FMTA0L2xvZ21vZGVsczIucGRmKSBmb3IgZ3VpZGFuY2Ugb24gaW50ZXByZXRpbmcgbG9nIHRyYW5zZm9ybWVkIHJpZ2h0IGhhbmQgc2lkZSB2YXJpYWJsZXMuIFNlZSBbaGVyZV0oaHR0cDovL3NlaXNtby5iZXJrZWxleS5lZHUvfmtpcmNobmVyL2Vwc18xMjAvVG9vbGtpdHMvVG9vbGtpdF8wMy5wZGYpIGZvciBhZGRpdGlvbmFsIGd1aWRhbmNlIG9uIGNob29zaW5nIGEgdHJhbnNmb3JtYXRpb24uCgoqKkhvdyB0byBpbnRlcnByZXQgUkhTIGxvZyB0cmFuc2Zvcm0qKjogRm9yIGEgbGluZWFyIG11bHRpdmFyaWF0ZSBPTFMgcmVncmVzc2lvbiwgd2Ugc2F5ICJhIG9uZSB1bml0IGluY3JlYXNlIGluIFggY2F1c2VzIGEgKGNvZWZmaWNpZW50KSBjaGFuZ2UgaW4gWS4iIEZvciBhIGxpbmVhci1sb2cgcmVncmVzc2lvbiB3aGVyZSB0aGUgWCB2YXJpYWJsZSBpcyBsb2cgdHJhbnNmb3JtZWQsIHdlIHNheSBhIEwgcGVyY2VudCBjaGFuZ2UgaW4gWCBsZWFkcyB0byBhIChjb2VmZmljaWVudCpMKSBjaGFuZ2UgaW4gWS4KCioqUXVlc3Rpb24qKjogRG8gd2Ugd2FudCB0byBydW4gdGhpcyBhbmFseXNpcyBmb3Igb25seSBPQUYgZmFybWVycz8gSWYgc28sIGFkanVzdCB0aGUgc2FtcGxlIGFjY29yZGluZ2x5LgoKYGBge3IgcmVzdWx0cz0nYXNpcycsIGZpZy5hbGlnbj0nY2VudGVyJ30KCmxvZ1ZhcnMgPC0gcGFzdGUobmFtZXMoZFtncmVwKCJsb2ciLCBuYW1lcyhkKSldKSwgY29sbGFwc2U9IiArICIpCgpsaXN0MiA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkWyx4XSB+IiwgIGxvZ1ZhcnMsICIrIGFzLmZhY3RvcihjZWxsKSIsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKCmxpc3QyYiA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkWyx4XSB+IiwgIGxvZ1ZhcnMsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJywgZXZhbD1GfQpzdXBwcmVzc1dhcm5pbmdzKAogIHN0YXJnYXplcihsaXN0MiwgbGlzdDJiLCB0eXBlPSJodG1sIiwgCiAgICAgICAgICB0aXRsZSA9ICIyMDE2QSBSd2FuZGEgU29pbCBIZWFsdGggQmFzZWxpbmUgLSBMb2cgQWdyb25vbWljIFByYWN0aWNlIE1vZGVscyIsCiAgICAgICAgICBjb3ZhcmlhdGUubGFiZWxzID0gYygiU2Vhc29ucyBvZiBGZXJ0aWxpemVyIChsb2cpIiwgIlNlYXNvbnMgb2YgQ29tcG9zdCAobG9nKSIsICJTZWFzb25zIG9mIExpbWUgKGxvZykiLCAiU2Vhc29ucyBvZiBGYWxsb3cgKGxvZykiKSwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKHJlcChnc3ViKCJtMy4iLCIiLCBzb2lsVmFycyksMikpLAogICAgICAgICAgZGVwLnZhci5jYXB0aW9uID0gIiIsCiAgICAgICAgICBkZXAudmFyLmxhYmVscyA9IGMoIiIsIiIpLAogICAgICAgICAgYWRkLmxpbmVzID0gbGlzdChjKCJDZWxsIEZFPyIsIHJlcCgiWWVzIiwgNSksIHJlcCgiTm8iLCA1KSkpLAogICAgICAgICAgbm90ZXMgPSAiSW5jbHVkZXMgRkUgZm9yIGNlbGwiLAogICAgICAgICAgb21pdD1jKCJjZWxsIiksIG91dD1wYXN0ZSgib3V0cHV0IiwgInJ3X2Jhc2VsaW5lX2FncHJhY19sb2cuaHRtIiwgc2VwPSIvIikpCikKYGBgCgpgYGB7cn0KcGxtLmxvZyA8LSBmdW5jdGlvbih4LCByYW5nZSl7CiAgCiAgYmV0YSA9IHJvdW5kKHN1bW1hcnkoeCkkY29lZmZpY2llbnRzW3JhbmdlLDFdLDMpCiAgYmV0YS5wdmFsID0gc3VtbWFyeSh4KSRjb2VmZmljaWVudHNbcmFuZ2UsNF0KICBiZXRhLmNvbnYgPSBpZmVsc2UoYmV0YS5wdmFsIDwgMC4wMSwgIioqKiIsIGlmZWxzZSgKICAgIGJldGEucHZhbCA8IDAuMDUsICIqKiIsIGlmZWxzZSgKICAgICAgYmV0YS5wdmFsIDwgMC4xLCAiKiIsICIiKSkpCiAgI2JldGEucHZhbCA9IHJvdW5kKGJldGEucHZhbCwgMykKICBvdXRjb21lID0gcGFzdGUoYmV0YSwgYmV0YS5jb252LCBzZXAgPSAiIikKICBvdXRjb21lID0gYyhvdXRjb21lLCB1bmlxdWUocm91bmQoc3VtbWFyeSh4KSRjb2VmZmljaWVudHNbMSwxXSwzKSkpCiAgcmVzID0gZGF0YS5mcmFtZShvdXRjb21lLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICByZXR1cm4ocmVzKSAgCn0KCnJ3LnRhYmxlMTUgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGxpc3QyLCBmdW5jdGlvbih4KXsKICBwbG0ubG9nKHgsIDI6NSkKfSkpCgpjb2xuYW1lcyhydy50YWJsZTE1KSA8LSBzb2lsVmFycwpyb3duYW1lcyhydy50YWJsZTE1KSA8LSBjKG5hbWVzKGQpW2dyZXAoImxvZyIsIG5hbWVzKGQpKV0sICJjb25zdGFudCIpCmBgYAoKIyMgVGFibGUgMTUgb3V0Y29tZXMKCmBgYHtyfQpydy50YWJsZTE1IDwtIHJ3LnRhYmxlMTVbLGMoInBIIiwgIlRvdGFsLkMiLCAiVG90YWwuTiIsICJtMy5DYSIsICJtMy5NZyIsICJFeEFsIildCgp3cml0ZS5jc3YocncudGFibGUxNSwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInJ3dGFibGUxNV9yZWcuY3N2Iiwgc2VwID0gIi8iKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IFQpCmBgYAoKCmBgYHtyfQojIGEgMTAgcGVyY2VudCBpbmNyZWFzZSBpbiB4IGxlYWRzIHRvIEIxKi4xIGNoYW5nZSBpbiBZCmxvZ1RyYW5zIDwtIGRvLmNhbGwoY2JpbmQsIGxhcHBseShsaXN0MiwgZnVuY3Rpb24oeCl7CiAgY29lZmYgPSB4JGNvZWZmaWNpZW50c1syOjVdCiAgdGVuUGVyY2VudCA9IHJvdW5kKGNvZWZmKi4xLCA1KQogIG5hbWVzKHRlblBlcmNlbnQpIDwtIHBhc3RlKCIxMCUgaW5jcmVhc2UgaW4gIiwgZ3N1YigibG9nIiwiIiwgbmFtZXModGVuUGVyY2VudCkpLCAiIGxlYWRzIHRvOiIsIHNlcD0iIikKICByZXR1cm4odGVuUGVyY2VudCkKfSkpCmNvbG5hbWVzKGxvZ1RyYW5zKSA8LSBzb2lsVmFycwpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZShsb2dUcmFucykpCmBgYAoKCioqSW50ZXJwcmV0YXRpb24qKjogV2hlbiB3ZSB0cmFuc2Zvcm0gdGhlIHZhcmlhYmxlcyB0byBsb2csIHRoZSBkYXRhIHN0YXJ0cyB0byB0ZWxsIGEgbW9yZSBjb2hlcmVudCBzdG9yeSwgYXQgbGVhc3QgZGlyZWN0aW9uYWxseS4gSWYgd2UgcmVtb3ZlIHRoZSBkaXN0cmljdCBGRSwgdGhlIGNvZWZmaWNpZW50cyBnYWluIHNpZ25pZmljYW5jZS4KCiogQWRkaXRpb25hbCBzZWFzb25zIG9mIGZlcnRpbGl6ZXIgdXNlIHJlbGF0ZXMgdG8gYSBkZWNyZWFzZSBpbiBzb2lsIE4gYW5kIEMuIAoqIFNlYXNvbnMgb2YgY29tcG9zdCBhbmQgbGltZSBoYXZlIHRoZSBvcHBvc2l0ZSwgcG9zaXRpdmUgZWZmZWN0IG9uIHNvaWwgTi4gCiogVW5zdXJwcmlzaW5nbHksIHNvaWwgaGVhbHRoIG1ldHJpY3MgYXJlIHBvb3JlciBmb3Igc29pbHMgb24gd2hpY2ggbW9yZSBmZXJ0aWxpemVyIGhhcyBiZWVuIHVzZWQgYW5kIGxpbWUgaGFzIG5vdCBiZWVuIHVzZWQuIAoKIyMjIFRlbnVyZSB3aXRoIE9uZSBBY3JlIEZ1bmQKTGV0J3MgbG9vayBmaXJzdCBhdCBhIG5haXZlIG1vZGVsIG9mIE9uZSBBY3JlIEZ1bmQgdGVudXJlIG9uIHNvaWwgaGVhbHRoLiAqKlJlbWVtYmVyKio6IHRoZXNlIGRhdGEgYXJlIG5vdCBsb25naXR1ZGluYWwhIFRoZXNlIGRhdGEgYXJlIG5vdCBsb25naXR1ZGluYWwgYW5kIHJlZmxlY3QgZmFybWVyIHNlbGVjdGlvbiBpbnRvIE9uZSBBY3JlIEZ1bmQuIFdoaWxlIHRoZXNlIG1vZGVscyB3aWxsIHRyeSB0byBiZSBib3RoIHJvYnVzdCBhbmQgcGFyc2ltb25pb3VzLCB3ZSB3aWxsIGluZXZpdGFiaWx5IHN1ZmZlciBvbWl0dGVkIHZhcmlhYmxlIGJpYXMgZHVlIHRvIGEgbGFjayBvZiBhbiBpbnN0cnVtZW50LgoKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQoKbGlzdDMgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsICAidG90YWwuc2Vhc29ucyArIGFzLmZhY3RvcihjZWxsKSIsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKCnN0YXJnYXplcihsaXN0MywgdHlwZT0iaHRtbCIsIAogICAgICAgICAgdGl0bGUgPSAiMjAxNkEgUndhbmRhIFNvaWwgSGVhbHRoIEJhc2VsaW5lIC0gTmFpdmUgVGVudXJlIE1vZGVscyIsCiAgICAgICAgICBjb3ZhcmlhdGUubGFiZWxzID0gYygiT0FGIFRlbnVyZSIpLAogICAgICAgICAgZGVwLnZhci5jYXB0aW9uID0gIiIsCiAgICAgICAgICBkZXAudmFyLmxhYmVscyA9ICIiLAogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoZ3N1YigibTMuIiwiIiwgc29pbFZhcnMpKSwKICAgICAgICAgIG5vdGVzID0gIkluY2x1ZGVzIEZFIGZvciBjZWxsIiwKICAgICAgICAgIG9taXQ9YygiY2VsbCIpLCBvdXQ9cGFzdGUoIm91dHB1dCIsICJyd19iYXNlbGluZV90ZW51cmUuaHRtIiwgc2VwPSIvIikpCmBgYAoKCioqSW50ZXJwcmV0YXRpb24qKjogVGhlIG5haXZlIE9uZSBBY3JlIEZ1bmQgdGVudXJlIG1vZGVsIHN1Z2dlc3QgdGhhdCBhY3Jvc3MgdGhlIGJvYXJkIHRoYXQgYWRkaXRpb25hbCB5ZWFycyBvZiAxQUYgcHJhY3RpY2VzIGhhdmUgYSBuZWdhdGl2ZSBlZmZlY3Qgb24gc29pbCBoZWFsdGggcGFyYW1ldGVycy4gTGV0J3MgY29tYmluZSAxQUYgdGVudXJlIHdpdGggdGhlIGFncm9ub21pYyBwcmFjdGljZXMgbW9kZWwgYWJvdmUgdG8gYnVpbGQgYSBtb3JlIHJvYnVzdCBtb2RlbDoKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpsaXN0NCA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkWyx4XSB+IiwgIGxvZ1ZhcnMsICIrIHRvdGFsLnNlYXNvbnMgKyBhcy5mYWN0b3IoY2VsbCkiLCBzZXA9IiIpKSwgZGF0YT1kKQogIHJldHVybihtb2QpCn0pCgpzdGFyZ2F6ZXIobGlzdDQsIHR5cGU9Imh0bWwiLCAKICAgICAgICAgIHRpdGxlID0gIjIwMTZBIFJ3YW5kYSBTb2lsIEhlYWx0aCBCYXNlbGluZSAtIEFnIFByYWN0aWNlIGFuZCBUZW51cmUiLAogICAgICAgICAgY292YXJpYXRlLmxhYmVscyA9IGMoIlNlYXNvbnMgb2YgRmVydGlsaXplciAobG9nKSIsICJTZWFzb25zIG9mIENvbXBvc3QgKGxvZykiLCAiU2Vhc29ucyBvZiBMaW1lIChsb2cpIiwgIlNlYXNvbnMgb2YgRmFsbG93IChsb2cpIiwgIk9BRiBUZW51cmUiKSwKICAgICAgICAgIGRlcC52YXIuY2FwdGlvbiA9ICIiLAogICAgICAgICAgZGVwLnZhci5sYWJlbHMgPSAiIiwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKGdzdWIoIm0zLiIsIiIsIHNvaWxWYXJzKSksCiAgICAgICAgICBub3RlcyA9ICJJbmNsdWRlcyBGRSBmb3IgY2VsbCIsCiAgICAgICAgICBvbWl0PWMoImNlbGwiKSwgb3V0PXBhc3RlKCJvdXRwdXQiLCAicndfYmFzZWxpbmVfYWdfdGVudXJlLmh0bSIsIHNlcD0iLyIpKQoKYGBgCgoqKkludGVycHJldGF0aW9uKio6IEluY2x1ZGluZyBhZ3Jvbm9taWMgcHJhY3RpY2VzIGFuZCAxQUYgdGVudXJlIGluIHRoZSBzYW1lIG1vZGVsIGRhbXBlbnMgdGhlIG1hZ25pdHVkZSwgYnV0IG5vdCB0aGUgc2lnbmlmaWNhbmNlLCBvZiAxQUYgdGVudXJlIG9uIHNvaWwgaGVhbHRoIG91dGNvbWVzLiAKCiMjIEFncm9ub21pYyBwcmFjdGljZXMgLSAxNUIgY3VsdGl2YXRpb24gcHJhY3RpY2VzClRodXMgZmFyIHdlIGhhdmUgbG9va2VkIGF0IGFnZ3JlZ2F0ZWQgaGlzdG9yaWNhbCBwbG90IGxldmVsIHByYWN0aWNlcyBhbmQgdGhlaXIgZWZmZWN0IG9uIHNvaWwgaGVhbHRoLiBXZSBhbHNvIGFza2VkIGZhcm1lcnMgYWJvdXQgdGhlaXIgY3VsdGl2YXRpb24gcHJhY3RpY2VzIG9uIHRoZWlyIHBsb3QgaW4gdGhlIHByZXZpb3VzIHNlYXNvbiwgMTVCLiBXZSBoYXZlIG1vcmUgcHJlY2lzZSBpbmZvcm1hdGlvbiBmb3IgZmVydGlsaXplciwgY29tcG9zdCBhbmQgbGltaW5nIHByYWN0aWNlcyBmb3IgdGhlIDE1QiBzZWFzb24uCgpgYGB7cn0KIyBzY2FsZSBhbGwgdGhlIGZpZWxkIGFwcGxpY2F0aW9uIHZhcmlhYmxlcyB0byBhcmVzCmQkYXJlcyA8LSBkJGZpZWxkLnNpemUvMTAwCmQkZmVydDEuYXJlIDwtIGQkZmllbGRfa2dfZmVydDFfMTViL2QkYXJlcwpkJGZlcnQyLmFyZSA8LSBkJGZpZWxkX2tnX2ZlcnQyXzE1Yi9kJGFyZXMKZCRmZXJ0LnRvdGFsLmFyZSA8LSBhcHBseShkWyxjKCJmZXJ0MS5hcmUiLCAiZmVydDIuYXJlIildLCAxLCBmdW5jdGlvbih4KXsKICBzdW0oeCwgbmEucm09VCkKfSkKCmQkZmVydC50b3RhbC5hcmUgPC0gaWZlbHNlKGlzLm5hKGQkZmVydDEuYXJlKSAmIGlzLm5hKGQkZmVydDIuYXJlKSwgTkEsIGQkZmVydC50b3RhbC5hcmUpCgpkJGNvbXBvc3QuYXJlIDwtIGQkZmllbGRfa2dfY29tcG9zdF8xNWIvZCRhcmVzCmQkbGltZS5hcmUgPC0gZCRrZ19saW1lXzE1Yi9kJGFyZXMKCmludGVuc2l0eVZhcnMgPC0gYygiZmVydDEuYXJlIiwgImZlcnQyLmFyZSIsCiAgICAgICAgICAgICAgICAgICAiY29tcG9zdC5hcmUiLCAibGltZS5hcmUiKQoKY29yKGRbLGludGVuc2l0eVZhcnNdLCB1c2U9ImNvbXBsZXRlLm9icyIpCmBgYAoKCmBgYHtyfQojdGFibGUoZCRmaWVsZF8xNWJfZmVydF90LCB1c2VOQSA9ICdpZmFueScpCmQkZmllbGRfMTViX2ZlcnRfdCA8LSB0b2xvd2VyKGQkZmllbGRfMTViX2ZlcnRfdCkKbmFtZXMoZClbbmFtZXMoZCk9PSJ2NjkiXSA8LSAiZmllbGRfMTViX2ZlcnRfdDIiCiN0YWJsZShkJGZpZWxkXzE1Yl9mZXJ0X3QyKQpkJGZpZWxkXzE1Yl9mZXJ0X3QyIDwtIHRvbG93ZXIoZCRmaWVsZF8xNWJfZmVydF90MikKYGBgCgpgYGB7cn0KZCRkYXAxIDwtIGlmZWxzZShkJGZpZWxkXzE1Yl9mZXJ0X3Q9PSJkYXAiLCBkJGZpZWxkX2tnX2ZlcnQxXzE1YiwgTkEpCmQkZGFwMS5hcmUgPC0gZCRkYXAxL2QkYXJlcwpkJG5wazEgPC0gaWZlbHNlKGdyZXBsKCJucGsiLCBkJGZpZWxkXzE1Yl9mZXJ0X3QpLCBkJGZpZWxkX2tnX2ZlcnQxXzE1YiwgTkEpCmQkbnBrMS5hcmUgPC0gZCRucGsxL2QkYXJlcwpkJHVyZWExIDwtIGlmZWxzZShkJGZpZWxkXzE1Yl9mZXJ0X3Q9PSJ1cmVhIiwgZCRmaWVsZF9rZ19mZXJ0MV8xNWIsIE5BKQpkJHVyZWExLmFyZSA8LSBkJHVyZWExL2QkYXJlcwpgYGAKCmBgYHtyfQpkJHVyZWEyIDwtIGlmZWxzZShkJGZpZWxkXzE1Yl9mZXJ0X3QyPT0idXJlYSIsIGQkZmllbGRfa2dfZmVydDJfMTViLCBOQSkKZCR1cmVhMi5hcmUgPC0gZCR1cmVhMi9kJGFyZXMKYGBgCgpgYGB7cn0KZCR0b3RhbC51cmVhLmFyZSA8LSBhcHBseShkWyxjKCJ1cmVhMS5hcmUiLCAidXJlYTIuYXJlIildLCAxLCBmdW5jdGlvbih4KXsKICBzdW0oeCwgbmEucm09VCkKfSkKZCR0b3RhbC51cmVhLmFyZSA8LSBpZmVsc2UoaXMubmEoZCR1cmVhMS5hcmUpICYgaXMubmEoZCR1cmVhMi5hcmUpLCBOQSwgZCR0b3RhbC51cmVhLmFyZSkKYGBgCgojIyMgR3JhcGggdGhlIGFwcGxpY2F0aW9uL2FyZSB2YXJpYWJsZXMKCmBgYHtyfQpmb3IoaSBpbiAxOmxlbmd0aChpbnRlbnNpdHlWYXJzKSl7CiAgcHJpbnQoCiAgZ2dwbG90KGQsIGFlcyh4PWRbLGludGVuc2l0eVZhcnNbaV1dKSkgKyBnZW9tX2RlbnNpdHkoKSArCiAgICAgIGxhYnMoeCA9IGludGVuc2l0eVZhcnNbaV0sIHRpdGxlID0gaW50ZW5zaXR5VmFyc1tpXSkKICApCn0KYGBgCgoqKkNvbmNsdXNpb24gLSBUYWtlIDEqKjogVGhlIGFwcGxpY2F0aW9uIHJhdGUgcGVyIGFyZSB2YXJpYWJsZXMgYXJlIHdlaXJkLiBJIHRoaW5rIGl0J3MgYmVjYXVzZSBvZiB0aGUgZmllbGQgZGltZW5zaW9ucy4gSSdtIGdvaW5nIHRvIGdvIGJhY2sgdG8gdGhlIGZpZWxkIGRpbWVuc2lvbnMgYW5kIGNoZWNrIHRoaXMuIAoKTGV0J3MgbG9vayBhdCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgZmllbGRzIHRoYXQgaGF2ZSBsYXJnZSBhcHBsaWNhdGlvbiByYXRlcwpgYGB7cn0KZFtkJGZlcnQxLmFyZT4xMCAmICFpcy5uYShkJGZlcnQxLmFyZSksIGMoImZpZWxkX2RpbTEiLCAiZmllbGRfZGltMiIsICJmaWVsZC5zaXplIiwgImZlcnQxLmFyZSIpXQoKZFtkJGZlcnQyLmFyZT4xMCAmICFpcy5uYShkJGZlcnQyLmFyZSksIGMoImZpZWxkX2RpbTEiLCAiZmllbGRfZGltMiIsICJmaWVsZC5zaXplIiwgImZlcnQyLmFyZSIpXQoKZFtkJGNvbXBvc3QuYXJlPjUwMCAmICFpcy5uYShkJGNvbXBvc3QuYXJlKSwgYygiZmllbGRfZGltMSIsICJmaWVsZF9kaW0yIiwgImZpZWxkLnNpemUiLCAiY29tcG9zdC5hcmUiKV0KIyB0aGVyZSdzIGEgZmllbGQgdGhhdCBpcyAxIG1ldGVyIHdpZGU/IFN1cmVseSBub3QuCgpkW2FicyhkJGxpbWUuYXJlKT40MCAmICFpcy5uYShkJGxpbWUuYXJlKSwgYygiZmllbGRfZGltMSIsICJmaWVsZF9kaW0yIiwgImZpZWxkLnNpemUiLCAibGltZS5hcmUiKV0KIyBob3cgaXMgdGhlcmUgYSBuZWdhdGl2ZSBxdWFudGl0eSBvZiBsaW1lPwpgYGAKCiMjIFRlbnVyZSBTY2F0dGVyIHBsb3RzCkdlbmVyYXRlIHRlbnVyZSB2cy4gcEggYW5kIHRlbnVyZSB2cy4gZmVydGlsaXplciBzZWFzb25zIHNjYXR0ZXIgcGxvdHM6CgpgYGB7cn0KdGVudXJlLnBoIDwtIGdncGxvdChkLCBhZXMoeD1qaXR0ZXIodG90YWwuc2Vhc29ucyksIHk9cEgpKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZD0ibG9lc3MiKSArCiAgbGFicyh4ID0gIk9BRiBUZW51cmUiLCB5ID0gIlNvaWwgcEgiLCB0aXRsZSA9ICJPQUYgVGVudXJlIHZzLiBzb2lsIHBIIGluIFJ3YW5kYSIpCgp0ZW51cmUucGgKCnBkZihwYXN0ZSgib3V0cHV0IiwgInJ3IHRlbnVyZSB2cyBwaC5wZGYiLCBzZXAgPSAiLyIpLCB3aWR0aD0xMSwgaGVpZ2h0PTguNSkKcHJpbnQodGVudXJlLnBoKQpkZXYub2ZmKCkKCmBgYAoKYGBge3J9CnRlbnVyZS5mZXJ0aWxpemVyIDwtIGdncGxvdChkLCBhZXMoeD0gaml0dGVyKHRvdGFsLnNlYXNvbnMpLCB5PWppdHRlcihuX3NlYXNvbl9mZXJ0KSkpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2Q9J2xvZXNzJykgKyAKICBsYWJzKHggPSAiT0FGIFRlbnVyZSIsIHkgPSAiWWVhcnMgb2YgRmVydGlsaXplciBpbiBwYXN0IDUgeWVhcnMiLCB0aXRsZSA9ICJPQUYgVGVudXJlIHZzLiBzb2lsIHBIIGluIFJ3YW5kYSIpCgp0ZW51cmUuZmVydGlsaXplcgoKcGRmKHBhc3RlKCJvdXRwdXQiLCAicncgdGVudXJlIHZzIGZlcnRpbGl6ZXIucGRmIiwgc2VwID0gIi8iKSwgd2lkdGg9MTEsIGhlaWdodD04LjUpCnByaW50KHRlbnVyZS5mZXJ0aWxpemVyKQpkZXYub2ZmKCkKYGBgCgoKIyMgVGFibGUgMTYgLSBLZ3Mgb2Ygc29pbCBhbW1lbmRtZW50cyAtIGZlcnRpbGl6ZXIgKGtnKQoKYGBge3J9CiMgdGhpcyBzaG91bGQgYmUga2cgb2YgZmVydGlsaXplciB1c2VkIGluIHRoaXMgZmllbGQuIENvbXBvc3QgaXMgb2ZmIHRoZSBjaGFydHMuIENvbnZlcnQgdGhpcyB0byBjb21wb3N0IHBlciBzcSBtZXRlcgpwbG0udDE2IDwtIGZ1bmN0aW9uKHgsIHJhbmdlKXsKICAKICBiZXRhID0gcm91bmQoc3VtbWFyeSh4KSRjb2VmZmljaWVudHNbcmFuZ2UsMV0sMykKICBiZXRhLnB2YWwgPSByb3VuZChzdW1tYXJ5KHgpJGNvZWZmaWNpZW50c1tyYW5nZSw0XSwzKQogIGJldGEuY29udiA9IGlmZWxzZShiZXRhLnB2YWwgPCAwLjAxLCAiKioqIiwgaWZlbHNlKAogICAgYmV0YS5wdmFsIDwgMC4wNSwgIioqIiwgaWZlbHNlKAogICAgICBiZXRhLnB2YWwgPCAwLjEsICIqIiwgIiIpKSkKICAjYmV0YS5wdmFsID0gcm91bmQoYmV0YS5wdmFsLCAzKQogIG91dGNvbWUgPSBwYXN0ZShiZXRhLCAiICgiLCBiZXRhLnB2YWwsICIpIiwgc2VwID0gIiIpCiAgb3V0Y29tZSA9IGMob3V0Y29tZSwgdW5pcXVlKHJvdW5kKHN1bW1hcnkoeCkkY29lZmZpY2llbnRzWzEsMV0sMykpKQogIHJlcyA9IGRhdGEuZnJhbWUob3V0Y29tZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiAgcmV0dXJuKHJlcykgIAp9CgoKcHJldmlvdXNTZWFzb25mZXJ0IDwtIHBhc3RlKCJmZXJ0MS5hcmUiLCAiYXMuZmFjdG9yKGNlbGwpIiwgc2VwPSIgKyAiKQoKbGlzdDUgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsIHByZXZpb3VzU2Vhc29uZmVydCwgc2VwPSIiKSksIGRhdGE9ZCkKICByZXR1cm4obW9kKQp9KQoKdGFibGUxNiA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkobGlzdDUsIGZ1bmN0aW9uKHgpewogIHBsbS50MTYoeCwgMikKfSkpCmNvbG5hbWVzKHRhYmxlMTYpIDwtIHNvaWxWYXJzCnJvd25hbWVzKHRhYmxlMTYpIDwtIGMoIkZlcnRpbGl6ZXIgKGtnL2FyZSkiLCAiY29uc3RhbnQiKQoKdGFibGUxNiA8LSB0YWJsZTE2WyxjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciLCAiRXhBbCIpXQp3cml0ZS5jc3YodGFibGUxNiwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInJ3dGFibGUxNl9mZXJ0LmNzdiIsIHNlcCA9ICIvIiksCiAgICAgICAgICByb3cubmFtZXMgPSBUKQpgYGAKCiMjIFRhYmxlIDE2IC0gYWxsIGZlcnRpbGl6ZXIgdXNlZCAKCmBgYHtyfQpwcmV2aW91c1NlYXNvbmZlcnRBbGwgPC0gcGFzdGUoImZlcnQudG90YWwuYXJlIiwgImFzLmZhY3RvcihjZWxsKSIsIHNlcD0iICsgIikKCmxpc3Q1YSA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkWyx4XSB+IiwgcHJldmlvdXNTZWFzb25mZXJ0QWxsLCBzZXA9IiIpKSwgZGF0YT1kKQogIHJldHVybihtb2QpCn0pCgp0YWJsZTE2YSA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkobGlzdDVhLCBmdW5jdGlvbih4KXsKICBwbG0udDE2KHgsIDIpCn0pKQpjb2xuYW1lcyh0YWJsZTE2YSkgPC0gc29pbFZhcnMKcm93bmFtZXModGFibGUxNmEpIDwtIGMoIkZlcnRpbGl6ZXIgKGFsbCkgKGtnL2FyZSkiLCAiY29uc3RhbnQiKQoKdGFibGUxNmEgPC0gdGFibGUxNmFbLGMoInBIIiwgIlRvdGFsLkMiLCAiVG90YWwuTiIsICJtMy5DYSIsICJtMy5NZyIsICJFeEFsIildCndyaXRlLmNzdih0YWJsZTE2YSwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInJ3dGFibGUxNl9mZXJ0X2FsbC5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzID0gVCkKYGBgCgojIyBUYWJsZSAxNiAtIEtncyBvZiBzb2lsIGFtbWVuZG1lbnRzIC0gY29tcG9zdCAoa2cpCgpgYGB7cn0KIyB0aGVzZSBvYmplY3RzIGhhdmUgdGhlIHNhbWUgbmFtZSBhcyB0aGUgZmVydGlsaXplciBvYmplY3RzIGZvciBzaW1wbGljaXR5LiBSdW4KIyBmcm9tIHRoZSB0b3AgdG8gYXZvaWQgb3ZlcndyaXRpbmcgaXNzdWVzLgpwcmV2aW91c1NlYXNvbmNvbXBvc3QgPC0gcGFzdGUoImNvbXBvc3QuYXJlIiwgImFzLmZhY3RvcihjZWxsKSIsIHNlcD0iICsgIikKdGVtcCA8LSBzdWJzZXQoZCwgZCRjb21wb3N0LmFyZT4wKQpsaXN0NWIgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgidGVtcFsseF0gfiIsIHByZXZpb3VzU2Vhc29uY29tcG9zdCwgc2VwPSIiKSksIGRhdGE9dGVtcCkKICByZXR1cm4obW9kKQp9KQoKdGFibGUxNmIgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGxpc3Q1YiwgZnVuY3Rpb24oeCl7CiAgcGxtLnQxNih4LCAyKQp9KSkKY29sbmFtZXModGFibGUxNmIpIDwtIHNvaWxWYXJzCnJvd25hbWVzKHRhYmxlMTZiKSA8LSBjKCJDb21wb3N0IChrZy9hcmUpIiwgImNvbnN0YW50IikKCnRhYmxlMTZiIDwtIHRhYmxlMTZiWyxjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciLCAiRXhBbCIpXQp3cml0ZS5jc3YodGFibGUxNmIsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJyd3RhYmxlMTZfY29tcG9zdC5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzID0gVCkKYGBgCgpUaGUgY29tcG9zdCByZWdyZXNzaW9uIHJlc3VsdHMgYXJlIHN1cHJpc2luZy4gTG9vayBhdCBDYXJib24gYW5kIGNvbXBvc3Qgc2NhdHRlciBwbG90LiBJZiB3ZSBpbmNsdWRlIDAgY29tcG9zdCwgd2Ugc2VlIG5vIHJlbGF0aW9uc2hpcC4gSWYgd2UgZXhjbHVkZSAwIGNvbXBvc3QsIHdlIAoKYGBge3J9CnNvaWxDLmNvbXBvc3QgPC0gZ2dwbG90KHRlbXAsIGFlcyh4ID0gY29tcG9zdC5hcmUsIHk9VG90YWwuQykpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDAsNTApKSArICMgcmVtb3ZlIGxhcmdlciB2YWx1ZXMgZnJvbSB0aGUgZ3JhcGguCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSdsbScpICsKICBsYWJzKHRpdGxlID0gIkNvbXBvc3QgYW5kIFNvaWwgQ2FyYm9uIGluIFJ3YW5kYSIsIHggPSAiQ29tcG9zdCAoa2cvYWNyZSkiLCB5ID0gIlRvdGFsIENhcmJvbiIpCgpzb2lsQy5jb21wb3N0CgpwZGYocGFzdGUoIm91dHB1dCIsICJjb21wb3N0IHZzIHNvaWwgYy5wZGYiLCBzZXAgPSAiLyIpLCB3aWR0aD0xMSwgaGVpZ2h0PTguNSkKcHJpbnQoc29pbEMuY29tcG9zdCkKZGV2Lm9mZigpCgpgYGAKClRoZXJlIGFyZSB0b28gbWFueSB6ZXJvIG9yIG5lYXIgemVybyB2YWx1ZXMuIFdlIGNhbiBjb25jbHVkZSB0aGF0IHBlcmhhcHMgd2UncmUgbm90IGFza2luZyB0aGlzIHF1ZXN0aW9uIHdlbGwuIEV2ZW4gd2hlbiB3ZSByZW1vdmUgbm9uLXplcm8gdmFsdWVzLCB0aGVyZSdzIGxpdHRsZSBkaXNjZXJuYWJsZSByZWxhdGlvbnNoaXAuCgojIyBUYWJsZSAxNiAtIEtncyBvZiBzb2lsIGFtbWVuZG1lbnRzIC0gbGltZSAoa2cpCgpgYGB7cn0KIyB0aGVzZSBvYmplY3RzIGhhdmUgdGhlIHNhbWUgbmFtZSBhcyB0aGUgZmVydGlsaXplciBvYmplY3RzIGZvciBzaW1wbGljaXR5LiBSdW4KIyBmcm9tIHRoZSB0b3AgdG8gYXZvaWQgb3ZlcndyaXRpbmcgaXNzdWVzLgpwcmV2aW91c1NlYXNvbmxpbWUgPC0gcGFzdGUoImxpbWUuYXJlIiwgImFzLmZhY3RvcihjZWxsKSIsIHNlcD0iICsgIikKbGlzdDVjIDwtIGxhcHBseShzb2lsVmFycywgZnVuY3Rpb24oeCl7CiAgbW9kIDwtIGxtKGFzLmZvcm11bGEocGFzdGUoImRbLHhdIH4iLCBwcmV2aW91c1NlYXNvbmxpbWUsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKCnRhYmxlMTZjIDwtIGRvLmNhbGwoY2JpbmQsIGxhcHBseShsaXN0NWMsIGZ1bmN0aW9uKHgpewogIHBsbS50MTYoeCwgMikKfSkpCmNvbG5hbWVzKHRhYmxlMTZjKSA8LSBzb2lsVmFycwpyb3duYW1lcyh0YWJsZTE2YykgPC0gYygiTGltZSAoa2cvYXJlKSIsICJjb25zdGFudCIpCgp0YWJsZTE2YyA8LSB0YWJsZTE2Y1ssYygicEgiLCAiVG90YWwuQyIsICJUb3RhbC5OIiwgIm0zLkNhIiwgIm0zLk1nIiwgIkV4QWwiKV0Kd3JpdGUuY3N2KHRhYmxlMTZjLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicnd0YWJsZTE2X2xpbWUuY3N2Iiwgc2VwID0gIi8iKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IFQpCmBgYAoKIyMgVGFibGUgMTYgLSB0eXBlcyBvZiBmZXJ0aWxpemVyIC0gREFQIChrZykKYGBge3J9CnByZXZpb3VzU2Vhc29uREFQIDwtIHBhc3RlKCJkYXAxLmFyZSIsICJhcy5mYWN0b3IoY2VsbCkiLCBzZXA9IiArICIpCmxpc3Q1ZCA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkWyx4XSB+IiwgcHJldmlvdXNTZWFzb25EQVAsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKCnRhYmxlMTZkIDwtIGRvLmNhbGwoY2JpbmQsIGxhcHBseShsaXN0NWQsIGZ1bmN0aW9uKHgpewogIHBsbS50MTYoeCwgMikKfSkpCmNvbG5hbWVzKHRhYmxlMTZkKSA8LSBzb2lsVmFycwpyb3duYW1lcyh0YWJsZTE2ZCkgPC0gYygiREFQIChrZy9hcmUpIiwgImNvbnN0YW50IikKCnRhYmxlMTZkIDwtIHRhYmxlMTZkWyxjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciLCAiRXhBbCIpXQp3cml0ZS5jc3YodGFibGUxNmQsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJyd3RhYmxlMTZfZGFwLmNzdiIsIHNlcCA9ICIvIiksCiAgICAgICAgICByb3cubmFtZXMgPSBUKQpgYGAKCiMjIFRhYmxlIDE2IC0gdHlwZXMgb2YgZmVydGlsaXplciAtIE5QSyAoa2cpCmBgYHtyfQpwcmV2aW91c1NlYXNvbk5QSyA8LSBwYXN0ZSgibnBrMS5hcmUiLCAiYXMuZmFjdG9yKGNlbGwpIiwgc2VwPSIgKyAiKQpsaXN0NWUgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsIHByZXZpb3VzU2Vhc29uTlBLLCBzZXA9IiIpKSwgZGF0YT1kKQogIHJldHVybihtb2QpCn0pCgp0YWJsZTE2ZSA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkobGlzdDVlLCBmdW5jdGlvbih4KXsKICBwbG0udDE2KHgsIDIpCn0pKQpjb2xuYW1lcyh0YWJsZTE2ZSkgPC0gc29pbFZhcnMKcm93bmFtZXModGFibGUxNmUpIDwtIGMoIk5QSyAoa2cvYXJlKSIsICJjb25zdGFudCIpCgp0YWJsZTE2ZSA8LSB0YWJsZTE2ZVssYygicEgiLCAiVG90YWwuQyIsICJUb3RhbC5OIiwgIm0zLkNhIiwgIm0zLk1nIiwgIkV4QWwiKV0Kd3JpdGUuY3N2KHRhYmxlMTZlLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicnd0YWJsZTE2X25way5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzID0gVCkKYGBgCgojIyBUYWJsZSAxNiAtIHR5cGVzIG9mIGZlcnRpbGl6ZXIgLSBVcmVhIChrZykKYGBge3J9CnByZXZpb3VzU2Vhc29uVXJlYSA8LSBwYXN0ZSgidG90YWwudXJlYS5hcmUiLCAiYXMuZmFjdG9yKGNlbGwpIiwgc2VwPSIgKyAiKQpsaXN0NWYgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsIHByZXZpb3VzU2Vhc29uVXJlYSwgc2VwPSIiKSksIGRhdGE9ZCkKICByZXR1cm4obW9kKQp9KQoKdGFibGUxNmYgPC0gZG8uY2FsbChjYmluZCwgbGFwcGx5KGxpc3Q1ZiwgZnVuY3Rpb24oeCl7CiAgcGxtLnQxNih4LCAyKQp9KSkKY29sbmFtZXModGFibGUxNmYpIDwtIHNvaWxWYXJzCnJvd25hbWVzKHRhYmxlMTZmKSA8LSBjKCJVcmVhIChrZy9hcmUpIiwgImNvbnN0YW50IikKCnRhYmxlMTZmIDwtIHRhYmxlMTZmWyxjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciLCAiRXhBbCIpXQp3cml0ZS5jc3YodGFibGUxNmYsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJyd3RhYmxlMTZfdXJlYS5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzID0gVCkKYGBgCgoKYGBge3IgcmVzdWx0cz0nYXNpcycsIGV2YWw9RkFMU0V9CnN0YXJnYXplcihsaXN0NSwgdHlwZT0iaHRtbCIsIAogICAgICAgICAgdGl0bGUgPSAiMjAxNkEgUndhbmRhIFNvaWwgSGVhbHRoIEJhc2VsaW5lIC0gMTVCIHByYWN0aWNlcyIsCiAgICAgICAgICBjb3ZhcmlhdGUubGFiZWxzID0gYygiRmVydGlsaXplciBSYXRlIChsb2cpIiksCiAgICAgICAgICBkZXAudmFyLmNhcHRpb24gPSAiIiwKICAgICAgICAgIGRlcC52YXIubGFiZWxzID0gIiIsCiAgICAgICAgICBjb2x1bW4ubGFiZWxzID0gYyhnc3ViKCJtMy4iLCIiLCBzb2lsVmFycykpLAogICAgICAgICAgbm90ZXMgPSAiSW5jbHVkZXMgRkUgZm9yIGNlbGwiLAogICAgICAgICAgb21pdD1jKCJjZWxsIiksIG91dD1wYXN0ZSgib3V0cHV0IiwgInJ3X2Jhc2VsaW5lXzE1Yl9hZy5odG0iLCBzZXA9Ii8iKSkKYGBgCgoKIyMgRmFybWVyIGZlcnRpbGl0eSBwZXJjZXB0aW9uCkxldCdzIGxvb2sgYXQgZmFybWVyIHBlcmNlaXZlZCBmZXJ0aWxpdHkgYXMgYSBwcmVkaWN0b3Igb2Ygc29pbCBoZWFsdGguIFdlJ2xsIHNldCAnc2FtZSBmZXJ0aWxpdHknIGFzIHRoZSByZWZlcmVuY2UgY2F0ZWdvcnkuCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KZCRmZXJ0aWxpdHlfcXVhbCA8LSByZWxldmVsKGFzLmZhY3RvcihkJGdlbmVyYWxfZmllbGRfaW5mb2NvbXBhcmVfZmVydGlsKSwgcmVmPSJzYW1lIikKCmxpc3Q2IDwtIGxhcHBseShzb2lsVmFycywgZnVuY3Rpb24oeCl7CiAgbW9kIDwtIGxtKGFzLmZvcm11bGEocGFzdGUoImRbLHhdIH4iLCAiKyBmZXJ0aWxpdHlfcXVhbCArIGFzLmZhY3RvcihjZWxsKSIsIHNlcD0iIikpLCBkYXRhPWQpCiAgcmV0dXJuKG1vZCkKfSkKCgpzdGFyZ2F6ZXIobGlzdDYsIHR5cGU9Imh0bWwiLCAKICAgICAgICAgIHRpdGxlID0gIjIwMTZBIFJ3YW5kYSBTb2lsIEhlYWx0aCBCYXNlbGluZSAtIEZhcm1lciBQZXJjZWl2ZWQgRmVydGlsaXR5IiwKICAgICAgICAgIGNvdmFyaWF0ZS5sYWJlbHMgPSBjKCJGYXJtZXIgT3BpbmlvbiAtIExlc3MgRmVydGlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFybWVyIE9waW5pb24gLSBNb3JlIEZlcnRpbGUiKSwKICAgICAgICAgIGRlcC52YXIuY2FwdGlvbiA9ICIiLAogICAgICAgICAgZGVwLnZhci5sYWJlbHMgPSAiIiwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKGdzdWIoIm0zLiIsIiIsIHNvaWxWYXJzKSksCiAgICAgICAgICBub3RlcyA9ICJSZWZlcmVuY2UgY2F0ZWdvcnkgaXMgc2FtZSBmZXJ0aWxpdHkgYXMgb3RoZXIgZmllbGRzLiBJbmNsdWRlcyBGRSBmb3IgY2VsbCIsCiAgICAgICAgICBub3Rlcy5hbGlnbiA9ICJsIiwKICAgICAgICAgIG9taXQ9YygiY2VsbCIpLCBvdXQ9cGFzdGUoIm91dHB1dCIsICJyd19iYXNlbGluZV9mZXJ0aWxpdHlfcXVhbC5odG0iLCBzZXA9Ii8iKSkKCmBgYAoKCioqSW50ZXJwcmV0YXRpb24qKjogRmFybWVycyB1bmRlcnN0YW5kIHRoZWlyIGZpZWxkcyB3ZWxsLiBUaGVpciBjYXRlZ29yaXphdGlvbiBvZiB3aGljaCBmaWVsZCBhcmUgbW9yZSBhbmQgbGVzcyBmZXJ0aWxlIGNvcnJlc3BvbmRzIHRvIG91ciBxdWFudGlmaWVkIG1lYXN1cmVzIG9mIHNvaWwgaGVhbHRoLiBUaGUgb25seSBmZWF0dXJlcyBmYXJtZXJzIGRvbid0IHNlZW0gdG8gZ2V0IGNvcnJlY3QgYXJlIG5pdHJvZ2VuIGFuZCBjYXJib24uIFRoZSBuaXRyb2dlbiBhbmQgY2FyYm9uIGxldmVscyBhcmUgaW5kaXN0aW5ndWlzaGFibGUgaW4gJ2xvdyBmZXJ0aWxpdHknIGZpZWxkcyByZWxhdGl2ZSB0byB0aGUgZmllbGRzIGRlZW1lZCB0byBiZSB0aGUgJ3NhbWUgZmVydGlsaXR5LicgKipSZW1pbmRlcioqOiBXZSBuZWVkIHRvIHJlbWVtYmVyIHRoYXQgZmFybWVycyBhcmUgb25seSBldmFsdXRpbmcgb25lIG9mIHRoZWlyIGZpZWxkcyB0aHVzIHdlIGFyZSBub3QgYWJsZSB0byBhY2NvdW50IGZvciB0aGUgcXVhbGl0eSBvZiB0aGUgZmFybWVyIGluIGFzc2Vzc2luZyBoaXMvaGVyIGZpZWxkcy4KCiMjIyBEZWVwZXIgZGl2ZSBpbnRvIGZhcm1lciBwZXJjZXB0aW9uIG9mIGZlcnRpbGl0eQoKKiBMb29rIGF0IHdldCBjaGVtIHZhbHVlcyBmb3IgYWxsIHNvaWwgZmVhdHVyZXMgYnkgZmFybWVyIHBlcmNlcHRpb24KKiBSdW4gUENBIG9uIGFsbCBzb2lsIGF0dHJpYnV0ZXMgdG8gc2VlIHdoaWNoIHByaW5jaXBsZSBjb21wb25lbnRzIHByZWRpY3Qgc29pbCBmZXJ0aWxpdHkKKiBDb25zaWRlciBhZGRpbmcgYWRkaXRpb25hbCBjb250cm9sIHZhcmlhYmxlcyBpbnRvIG1vZGVsIChsaWtlIENhIGFzIGNvbnRyb2wgZm9yIHBIIHBlcmNlcHRpb24gbW9kZWwpCgpgYGB7cn0KIyBtZXJnZSB3ZXRDaGVtIGluIHdpdGggZApuYW1lcyh3ZXRDaGVtKVsyOjIxXSA8LSBwYXN0ZSgid2V0LiIsIG5hbWVzKHdldENoZW0pWzI6MjFdLCBzZXAgPSAiIikKZCA8LSBsZWZ0X2pvaW4oZCwgd2V0Q2hlbSwgYnk9IlNTTiIpCmBgYAoKIyMjIEZvciBFcmljLCBDRUMgc3VtbWFyeSBmb3IgbmV3IENhIGFuZCBNZyB0aHJlc2hvbGRzCgpgYGB7cn0KZ2dwbG90KGQsIGFlcyh4PXdldC5DLkUuQykpICsgZ2VvbV9kZW5zaXR5KCkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWRpYW4oZCR3ZXQuQy5FLkMsIG5hLnJtPVQpLCBjb2xvcj0icmVkIikgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBtZWFuKGQkd2V0LkMuRS5DLCBuYS5ybT1UKSwgY29sb3I9ImJsdWUiKQoKc3VtbWFyeShkJHdldC5DLkUuQylbMzo0XQpgYGAKCgpgYGB7cn0Kd2V0VmFycyA8LSBuYW1lcyhkKVtncmVwKCJ3ZXQuIiwgbmFtZXMoZCkpXQpmb3IoaSBpbiAxOmxlbmd0aCh3ZXRWYXJzKSl7CiAgcHJpbnQoCiAgZ2dwbG90KGRhdGE9ZCwgYWVzKHg9YXMuZmFjdG9yKGNsaWVudCksIHk9ZFssd2V0VmFyc1tpXV0pKSArIAogICAgZ2VvbV9ib3hwbG90KCkgKwogICAgbGFicyh4PSJUdWJ1cmEgRmFybWVyIiwgeT13ZXRWYXJzW2ldLCB0aXRsZSA9IHBhc3RlKCJSVyBiYXNlbGluZSB3ZXQgY2hlbSAtICIsIHdldFZhcnNbaV0sIHNlcCA9ICIiKSkKICApCn0KCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9Cmxpc3Q3IDwtIGxhcHBseSh3ZXRWYXJzLCBmdW5jdGlvbih4KXsKICBtb2QgPC0gbG0oYXMuZm9ybXVsYShwYXN0ZSgiZFsseF0gfiIsICIrIGZlcnRpbGl0eV9xdWFsICsgYXMuZmFjdG9yKGNlbGwpIiwgc2VwPSIiKSksIGRhdGE9ZCkKICByZXR1cm4obW9kKQp9KQoKc3VwcHJlc3NXYXJuaW5ncygKc3RhcmdhemVyKGxpc3Q3LCB0eXBlPSJodG1sIiwgCiAgICAgICAgICB0aXRsZSA9ICIyMDE2QSBSd2FuZGEgU29pbCBIZWFsdGggQmFzZWxpbmUgLSBGYXJtZXIgUGVyY2VpdmVkIEZlcnRpbGl0eSAod2V0IGNoZW0pIiwKICAgICAgICAgIGNvdmFyaWF0ZS5sYWJlbHMgPSBjKCJGYXJtZXIgT3BpbmlvbiAtIExlc3MgRmVydGlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFybWVyIE9waW5pb24gLSBNb3JlIEZlcnRpbGUiKSwKICAgICAgICAgIGRlcC52YXIuY2FwdGlvbiA9ICIiLAogICAgICAgICAgZGVwLnZhci5sYWJlbHMgPSAiIiwKICAgICAgICAgIGNvbHVtbi5sYWJlbHMgPSBjKGdzdWIoIndldC4iLCIiLCB3ZXRWYXJzKSksCiAgICAgICAgICBub3RlcyA9ICJSZWZlcmVuY2UgY2F0ZWdvcnkgaXMgc2FtZSBmZXJ0aWxpdHkgYXMgb3RoZXIgZmllbGRzLiBJbmNsdWRlcyBGRSBmb3IgY2VsbCIsCiAgICAgICAgICBub3Rlcy5hbGlnbiA9ICJsIiwKICAgICAgICAgIG9taXQ9YygiY2VsbCIpLCBvdXQ9cGFzdGUoIm91dHB1dCIsICJyd19iYXNlbGluZV9mZXJ0aWxpdHlfcXVhbF93ZXQuaHRtIiwgc2VwPSIvIikpCikKYGBgCgoqKkludGVycHJldGF0aW9uKio6IE91ciBzYW1wbGUgc2l6ZSBkZWNyZWFzZXMgY29uc2lkZXJhYmx5IHdoZW4gd2UgbG9vayBvbmx5IGF0IHdldCBjaGVtaXN0cnkgcmVzdWx0cy4gVGh1cywgd2UgZG8gbm90IHNlZSB0aGUgc2FtZSBzaWduZmljYW50IHJlbGF0aW9uc2hpcHMgd2Ugc2F3IGJldHdlZW4gZmFybWVyIHBlcmNlaXZlZCBmZXJ0aWxpdHkgYW5kIHNvaWwgY2hhcmFjdGVyaXN0aWNzIHdlIHNhdyB3aGVuIGxvb2tpbmcgYXQgdGhlIHByZWRpY3RlZCB2YWx1ZXMuCgojIyBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChwcmVkaWN0ZWQgdmFsdWVzKQpgYGB7cn0KcGNhMSA8LSBwcmNvbXAoZFssc29pbFZhcnNdLCBzY2FsZT1UUlVFLCBjZW50ZXI9VFJVRSkKcHJpbnQocGNhMSkKcGxvdChwY2ExKQpgYGAKCmBgYHtyfQpwY2ExJHJvdGF0aW9uCmBgYAoKVGhlIGZpcnN0IHByaW5jaXBhbCBjb21wb25lbnQgaXMgY29tcG9zZWQgcHJpbWFyaWx5IG9mIHNvaWwgcEggcmVsYXRlZCB2YXJpYWJsZXMsIENhLCBNZywgYW5kIHBILiBUaGUgc2Vjb25kIGNhcHR1cmUgTiBhbmQgQy4gVGhlc2UgZ3JvdXBpbmdzIChwSCBncm91cGluZyBhbmQgQyBhbmQgTikgYXJlIG5vdCBhbGwgdGhhdCBzdXJwcmlzaW5nIGdpdmVuIHRoYXQgb3VyIHByZWRpY3RlZCBzb2lsIHZhcmlhYmxlIHNldCBpcyBmYWlybHkgbGltaXRlZC4KCklmIHRoZSB2YXJpYWJsZXMgaGF2ZSB0aGUgc2FtZSBzaWduIHRoYXQgaW5kaWNhdGVzIHRoYXQgdGhleSBhcmUgcG9zaXRpdmVseSBjb3JyZWxhdGVkIGluIHRoZSBwcmluY2lwYWwgY29tcG9uZW50LiBJbiB0aGUgZmlyc3QgcHJpbmNpcGFsIGNvbXBvbmVudCwgd2Ugc2VlIENhLCBNZyBhbmQgcEggbG9hZGluZyBpbiB0aGUgc2FtZSBkaXJlY3Rpb24uIEluIHRoZSBzZWNvbmQgcHJpbmNpcGFsIGNvbXBvbmVudCwgd2Ugc2VlIE4gYW5kIEMgbW92aW5nIGluIHRoZSBzYW1lIGRpcmVjdGlvbi4gQ2EgYW5kIE1nIGFyZSBhbHNvIGFzc29jaWF0ZWQgaW4gdGhlIHNhbWUgZGlyZWN0aW9uIGJ1dCB0byBhIGxlc3NlciBleHRlbnQuIAoKCmBgYHtyfQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShwY2ExJHgpLGFlcyh4PVBDMSx5PVBDMiwgY29sb3I9ZCRmZXJ0aWxpdHlfcXVhbCkpICsgZ2VvbV9wb2ludCgpICsgCiAgbGFicyh0aXRsZSA9ICJQQ0Egb2Ygc29pbCBhdHRyaWJ1dGVzIGFuZCBmYXJtZXIgcGVyY2VpdmVkIHNvaWwgZmVydGlsaXR5IiwKICAgICAgIHggPSAiRmlyc3QgUENBIiwgeT0gIlNlY29uZCBQQ0EiLCBjb2xvcj0iRmllbGQgUXVhbGl0eSIpCmBgYApXaGVuIHdlIGxheWVyIGZhcm1lciBwZXJjZWl2ZWQgc29pbCBmZXJ0aWxpdHkgb24gdG9wIG9mIHRoZSBwcmluY2lwYWwgY29tcG9uZW50IHNjYXR0ZXIgcGxvdCwgbm8gY2xlYXIgcGF0dGVybiBlbWVyZ3Jlcy4gRmllbGRzIHdpdGggdGhlIHNhbWUgZmVydGlsaXRpeSwgbGVzcyBhbmQgbW9yZSBmZXJ0aWxpdHkgYXJlIGluZGlzdGluZ3Vpc2hhYmxlIGJ5IHRoZSBwcmluY2lwYWwgY29tcG9uZW50cy4gVGh1cywgdGhlcmUgaXMgbm90IGEgY2xlYXIgcHJvZmlsZSBmb3IgZmFybWVyIGlkZW50aWZpZWQgaGVhbHRoaWVyIHNvaWwgb3Igd2Vha2VyIHNvaWwgYmFzZWQgb24gcHJpbmNpcGFsIGNvbXBvbmVudHMuCgojIyMgR2VvZ3JhcGhpYyBEZXNjcmlwdG9ycyAtIFBDQSBmb3Igc29pbCBwcm9maWxlcwpUaGlzIHNlY3Rpb24gd2lsbCBidWlsZCBvbiB0aGUgcHJpbmNpcGFsIGNvbXBvbmVudCB3b3JrIGFib3ZlIGFuZCBsb29rIGF0IGltcHJvdmluZyB1bmRlcnN0YW5kaW5nIG9mIGxvY2FsIGNvbnRleHQgdG8gaW5mb3JtIGxvY2FsIGFkYXB0YXRpb24uIEhlcmUgd2Ugd2lsbCBhbHNvIGNvbnN0cnVjdCBzb2lsIHByb2ZpbGVzIHRvIHNpbXBsaWZ5IHRoZSBzY2FsaW5nIG9mIHByb21pc2luZyBwcm9kdWN0cyBhbmQgcHJhY3RpY2VzIHRvIHRhcmdldGVkIGxvY2F0aW9ucy4gV2UgZG9uJ3QgaGF2ZSBhIGZ1bGwgc3VpdGUgb2YgcHJlZGljdG9ycyBzbyB3ZSBjYW4ndCBsb29rIGF0IGEgY29tcHJlaGVuc2l2ZSBzb2lsIHByb2ZpbGUuCgpgYGB7cn0KZ2dwbG90KGFzLmRhdGEuZnJhbWUocGNhMSR4KSxhZXMoeD1QQzEseT1QQzIsIGNvbG9yPWQkZGlzdHJpY3QpKSArIGdlb21fcG9pbnQoKSArIAogIGxhYnModGl0bGUgPSAiUENBIG9mIHNvaWwgYXR0cmlidXRlcyBhbmQgZGlzdHJpY3QiLAogICAgICAgeCA9ICJGaXJzdCBQQ0EiLCB5PSAiU2Vjb25kIFBDQSIsIGNvbG9yPSJEaXN0cmljdCIpCmBgYAoKV2hlbiB3ZSBjb2xvciB0aGUgZmlndXJlIGJ5IGRpc3RyaWN0LCB3ZSBzdGFydCB0byBzZWUgYSBwYXR0ZXJuIGVtZXJnZS4gSG93ZXZlciwgdGhlcmUgYXJlIHRvbyBtYW55IHBvaW50cyB0byBjbGVhcmx5IGRldGVjdCB3aGVyZSBhbGwgZGlzdHJpY3RzIGZhbGwuIExldCdzIGluc3RlYWQgbG9vayBhdCB0aGUgZGF0YSBieSBBRVouCgoKYGBge3J9CmdncGxvdChhcy5kYXRhLmZyYW1lKHBjYTEkeCksYWVzKHg9UEMxLHk9UEMyLCBjb2xvcj1kJGFleikpICsgZ2VvbV9wb2ludCgpICsgCiAgbGFicyh0aXRsZSA9ICJQQ0Egb2Ygc29pbCBhdHRyaWJ1dGVzIGFuZCBBRVoiLAogICAgICAgeCA9ICJGaXJzdCBQQ0EgLSBDYS9NZy9wSCIsIHk9ICJTZWNvbmQgUENBIC0gTiBhbmQgQyIsIGNvbG9yPSJBRVoiKQpgYGAKCkNvbG9yaW5nIHRoZSBwb2ludHMgYnkgQUVaLCB3ZSBzZWUgYSBtdWNoIGNsZWFyZXIgdHJlbmQuIFRoZSBlYXN0IGlzIHRvIHRoZSByaWdodCBvZiBvdXIgZ3JhcGhpYywgdGhlbiB0aGUgY2VudHJhbCBwbGF0ZWF1IGZvbGxvd2VkIGJ5IGxha2UgS2l2ZSBhbmQgdGhlbiBDb25nbyBOaWxlIGluIGdyZWVuIG9uIHRoZSBsZWZ0LiBXaGF0IGRvZXMgdGhpcyBtZWFuIGluIHRlcm1zIG9mIGFjdHVhbCBzb2lsIGZlYXR1cmVzPyBMZXQncyBsb29rIGF0IHRoZSBmaWd1cmUgYnV0IHdpdGggdGhlIHZhcmlhYmxlIGFzc29jaWF0aW9ucyBsYXllcmVkIG9uIHRvcC4gU2VlIGhlcmUgZm9yIFtkb2N1bWVudGF0aW9uXShodHRwOi8vc3RhdHMuc3RhY2tleGNoYW5nZS5jb20vcXVlc3Rpb25zLzg4ODgwL2RvZXMtdGhlLXNpZ24tb2Ytc2NvcmVzLW9yLW9mLWxvYWRpbmdzLWluLXBjYS1vci1mYS1oYXZlLWEtbWVhbmluZy1tYXktaS1yZXZlcnMpCgpgYGB7cn0KbGlicmFyeShwY2EzZCkKcGNhMmQoIHBjYTEsIGJpcGxvdD0gVFJVRSwgc2hhcGU9IDE5LCBjb2w9ICJibGFjayIgICkKYGBgCgpJdCBhcHBlYXJzIHRoYXQgdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIGdyYXBoLCB0aGUgZWFzdGVybiBBRVosIGlzIGFzc29jaWF0ZWQgd2l0aCBwSCwgTWcgYW5kIENhLiBUaGlzIGluZGljYXRlcyB0aGF0IGFzIHRoZSBmaXJzdCBwcmluY2lwYWwgY29tcG9uZW50IGluY3JlYXNlcywgc28gZG9lcyB0aGUgbGV2ZWwgb2YgcEgsIENhIGFuZCBNZy4gQ29udmVyc2VseSwgdG90YWwgTiBhbmQgQyBpbmNyZWFzZSBhcyB0aGUgc2Vjb25kIHByaW5jaXBhbCBjb21wb25lbnQgZGVjcmVhc2VzLiBJbiB0ZXJtcyBvZiB0aGUgQUVaIGZpZ3VyZSBhYm92ZSwgdGhpcyBzdWdnZXN0IHRoYXQgdGhlIENvbmdvIE5pbGUgQUVaIGhhcyBoaWdoZXIgbGV2ZWxzIG9mIE4gYW5kIEMuIExldCdzIHRlc3QgdGhlc2UgaHlwb3RoZXNlcyB3aXRoIGEgc2ltcGxlIHN1bW1hcnkgdGFibGU6CgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnQoa2FibGUoYWdncmVnYXRlKGRbLHNvaWxWYXJzXSwgYnk9bGlzdChkJGFleiksIGZ1bmN0aW9uKHgpewogIHJvdW5kKG1lYW4oeCwgbmEucm09VCksNSkKfSkpKQpgYGAKCkNvbmZpcm1lZCEgVGhpcyBsaWtlbHkgYWxzbyBzdWdnZXN0cyB0aGF0IENvbmdvIE5pbGUgaXMgYXQgYSBoaWdoZXIgYWx0aXR1ZGUgdGhhbiB0aGUgc3Vycm91bmRpbmcgYXJlYXMgYW5kIHRoYXQgZWFzdGVybiBSd2FuZGEgaGFzIHJlbGF0aXZlbHkgbGVzcyB3ZWF0aGVyZWQgc29pbHMgY29tcGFyZWQgdG8gd2VzdGVybi4KCioqQ29uc2VxdWVuY2UgZm9yIHRyaWFsIHBsYWNlbWVudCoqOiBUaGUgUndhbmRhIHByb2dyYW0gaXMgYWxyZWFkeSBibG9ja2luZyB0cmlhbHMgYnkgQUVaIGJ1dCB0aGVzZSBkYXRhIGNvbmZpcm0gdGhhdCBBRVogcmVmbGVjdHMgbWVhbmluZ2Z1IHNvaWwgdmFyaWF0aW9uIGFuZCB0aHVzIGNhcHR1cmVzIGtleSBncm93aW5nIGNvbmRpdGlvbnMgZm9yIFJ3YW5kYW4gZmFybWVycy4gQmxvY2tpbmcgdHJpYWxzIGJ5IEFFWiBpbiBSd2FuZGEgd2lsbCBlbmFibGUgdXMgdG8gZXZhbHVhdGUgdHJpYWwgaHlwb3RoZXNlcyBpbiBtb3JlIG5ldXRyYWwgcEggcmFuZ2VzIGFuZCBoaWdoZXIgTiBhbmQgQyBjb25kaXRpb25zLgoKIyMjIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKHdldCBjaGVtaXN0cnkgdmFsdWVzKQpgYGB7cn0Kd2V0VmFsIDwtIGRbY29tcGxldGUuY2FzZXMoZFssd2V0VmFyc10pLF0KCnBjYTIgPC0gcHJjb21wKHdldFZhbFssYygid2V0LkMuRS5DIiwgIndldC5wSCIsICJ3ZXQuTWciLCAid2V0LkNhIildLCBjZW50ZXI9VFJVRSwgc2NhbGU9VFJVRSkKCnBjYTIgPC0gcHJjb21wKHdldFZhbFssIHdldFZhcnNdLCBjZW50ZXI9VFJVRSwgc2NhbGU9VFJVRSkKYGBgCgoKYGBge3J9CiNwbG90KHBjYTIpCmdncGxvdChhcy5kYXRhLmZyYW1lKHBjYTIkeCksYWVzKHg9UEMxLHk9UEMyLCBjb2xvcj1hcy5mYWN0b3Iod2V0VmFsJGFleikpKSArIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJXZXQgQ2hlbSBQQ0Egd2l0aCBBRVoiLCB4ID0gIkZpcnN0IFBDIiwgeT0iU2Vjb25kIFBDIiwKICAgICAgIGNvbG9yPSJBRVoiKQojcGNhMiRyb3RhdGlvbgpgYGAKCmBgYHtyfQpwY2EyZChwY2EyLCBiaXBsb3Q9IFRSVUUsIHNoYXBlPSAxOSwgY29sPSAiYmxhY2siKQpgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQoKIyBwdXQgcGNhMiR4IFBDMSBpbiB0aGUgbWFpbiBkYXRhIHRvIHJ1biB0aGUgZmVydGlsaXR5IHBlcmNlcHRpb24gbW9kZWwuCiNtb2Q4IDwtIGxtKApzdXBwcmVzc1dhcm5pbmdzKApzdGFyZ2F6ZXIobGlzdDcsIHR5cGU9Imh0bWwiLCAKICAgICAgICAgIHRpdGxlID0gIjIwMTZBIFJ3YW5kYSBTb2lsIEhlYWx0aCBCYXNlbGluZSAtIEZhcm1lciBQZXJjZWl2ZWQgRmVydGlsaXR5ICh3ZXQgY2hlbSkiLAogICAgICAgICAgY292YXJpYXRlLmxhYmVscyA9IGMoIkZhcm1lciBPcGluaW9uIC0gTGVzcyBGZXJ0aWxlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGYXJtZXIgT3BpbmlvbiAtIE1vcmUgRmVydGlsZSIpLAogICAgICAgICAgZGVwLnZhci5jYXB0aW9uID0gIiIsCiAgICAgICAgICBkZXAudmFyLmxhYmVscyA9ICIiLAogICAgICAgICAgY29sdW1uLmxhYmVscyA9IGMoZ3N1Yigid2V0LiIsIiIsIHdldFZhcnMpKSwKICAgICAgICAgIG5vdGVzID0gIlJlZmVyZW5jZSBjYXRlZ29yeSBpcyBzYW1lIGZlcnRpbGl0eSBhcyBvdGhlciBmaWVsZHMuIEluY2x1ZGVzIEZFIGZvciBjZWxsIiwKICAgICAgICAgIG5vdGVzLmFsaWduID0gImwiLAogICAgICAgICAgb21pdD1jKCJjZWxsIiksIG91dD1wYXN0ZSgib3V0cHV0IiwgInJ3X2Jhc2VsaW5lX2ZlcnRpbGl0eV9xdWFsX3dldC5odG0iLCBzZXA9Ii8iKSkKKQpgYGAKCiMjIERpc3RyaWN0IGFuZCBjZWxsIGxldmVsIHN1bW1hcmllcyBvZiBzb2lsIGFuZCBtYW5hZ21lbnQgcHJhY3RpY2VzCmBgYHtyfQpkaXN0LnN1bSA8LSBhZ2dyZWdhdGUoZFssb3V0Lmxpc3RdLCBieT1saXN0KGQkZGlzdHJpY3QpLCBmdW5jdGlvbih4KXsKICByZXR1cm4oYygKICAgIHBhc3RlKAogICAgICByb3VuZChtZWFuKHgsIG5hLnJtPVQpLDMpLCAiICgiLCByb3VuZChtZWRpYW4oeCwgbmEucm09VCksMyksICIpIiwgCiAgICAgICIgKCIsIHJvdW5kKHNkKHgsIG5hLnJtPVQpLDIpLCAiKSIsIHNlcCA9ICIiCiAgICApCiAgICApKQp9KQpgYGAKCmBgYHtyfQp3cml0ZS5jc3YoZGlzdC5zdW0sIGZpbGU9cGFzdGUoIm91dHB1dCIsICJkaXN0cmljdCBjb3ZhcmlhdGUgc3VtbWFyeS5jc3YiLCBzZXAgPSAiLyIpKQpgYGAKCmBgYHtyfQpjZWxsLnN1bSA8LSBhZ2dyZWdhdGUoZFssb3V0Lmxpc3RdLCBieT1saXN0KGQkY2VsbCksIGZ1bmN0aW9uKHgpewogIHJldHVybihjKAogICAgcGFzdGUoCiAgICAgIHJvdW5kKG1lYW4oeCwgbmEucm09VCksMyksICIgKCIsIHJvdW5kKG1lZGlhbih4LCBuYS5ybT1UKSwzKSwgIikiLCAKICAgICAgIiAoIiwgcm91bmQoc2QoeCwgbmEucm09VCksMiksICIpIiwgc2VwID0gIiIKICAgICkKICAgICkpCn0pCmBgYAoKYGBge3J9CndyaXRlLmNzdihjZWxsLnN1bSwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgImNlbGwgY292YXJpYXRlIHN1bW1hcnkuY3N2Iiwgc2VwID0gIi8iKSkKYGBgCgojIyBGZW1hbGUgZmFybWVycyBmYXJtaW5nIHBvb3JlciBzb2lscz8KYGBge3J9CnJlbW92ZSA8LSAiZmVtYWxlIgpnZW5kZXJCYWxhbmNlIDwtIG91dC5saXN0WyFvdXQubGlzdCAlaW4lIHJlbW92ZV0KCmVxdWFsIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShnZW5kZXJCYWxhbmNlLCBmdW5jdGlvbih4KXsKICAgIAogICAgb3V0IDwtIHQudGVzdChkWyx4XSB+IGRbLCJmZW1hbGUiXSwgZGF0YT1kKQogICAgdGFiIDwtIGRhdGEuZnJhbWUob3V0W1s1XV1bWzJdXSxvdXRbWzVdXVtbMV1dLCBvdXRbM10pCiAgICB0YWJbLDE6Ml0gPC0gcm91bmQodGFiWywxOjJdLDMpCiAgICBuYW1lcyh0YWIpIDwtIGMobmFtZXMob3V0W1s1XV0pLCAicHZhbHVlIikKICAgICN0YWJbLDNdIDwtIHAuYWRqdXN0KHRhYlssM10sIG1ldGhvZD0iaG9sbSIpCiAgICAjdGFiWywzXSA8LSBpZmVsc2UodGFiWywzXSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKHRhYlssM10sMykpCiAgICAjcHJpbnQodGFiKQogICAgcmV0dXJuKHRhYikKICAKfSkpCgpyb3duYW1lcyhlcXVhbCkgPC0gTlVMTAoKIyBvcmRlciB2YXJpYWJsZXMgCmVxdWFsJHB2YWx1ZSA8LSBwLmFkanVzdChlcXVhbCRwdmFsdWUsIG1ldGhvZD0iZmRyIikKcm93bmFtZXMoZXF1YWwpIDwtIGdlbmRlckJhbGFuY2UKZXF1YWwgPC0gZXF1YWxbb3JkZXIoZXF1YWwkcHZhbHVlKSxdCgplcXVhbCRwdmFsdWUgPC0gaWZlbHNlKGVxdWFsJHB2YWx1ZSA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKGVxdWFsJHB2YWx1ZSwzKSkKY29sbmFtZXMoZXF1YWwpIDwtIGMoIk1hbGUgRmFybWVycyIsIkZlbWFsZSBGYXJtZXJzIiwgInAtdmFsdWUiKQkKYGBgCgpgYGB7cn0Kd3JpdGUuY3N2KGVxdWFsLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicncgZmVtYWxlIGZhcm1lciBzdGF0dXMuY3N2Iiwgc2VwID0gIi8iKSwgcm93Lm5hbWVzPVQpCmBgYAoKIyMgRXhwb3J0IHRoZSBkYXRhIGZvciBFcmljICAKICAKYGBge3J9CndyaXRlLmNzdihkLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicndhbmRhX3Noc19tZXJnZWRfY2xlYW4uY3N2IixzZXAgPSAiLyIpLCByb3cubmFtZXMgPSBGKQpgYGAKCiMgUHJvcGVuc2l0eSBTY29yZSBNYXRjaGluZwpXZSBuZWVkIHRvIGRvIGEgbW9yZSByaWdvcm91cyBqb2Igb2YgYWNjb3VudGluZyBmb3IgZGlmZmVyZW5jZXMgYmV0d2VlbiBUdWJ1cmEgZmFybWVycyBhbmQgaWRlbnRpZmllZCBjb250cm9sIGZhcm1lcnMuIEV4ZWN1dGUgcHJvcGVuc2l0eSBzY29yZSBtYXRjaGluZyAoUFNNKSB0byBpZGVudGlmeSBjb250cm9sIGZhcm1lcnMgdGhhdCBvdmVybGFwIHdpdGggVHVidXJhIGZhcm1lcnMgd2l0aCByZWdhcmQgdG8gdGhlaXIgbGlrZWxpaG9vZCBvZiBiZWluZyBhIFR1YnVyYSBmYXJtZXIuCgpgYGB7cn0KcHNtVmFycyA8LSBwYXN0ZShjKCJmZW1hbGUiLCAiYWdlIiwgImhoc2l6ZSIsICJ0b3RhbC5zZWFzb25zIiwKICAgICAgICAgICAgICAgICAgICJjb3dzIiwgImdvYXRzIiwgImNoaWNrZW5zIiwgInBpZ3MiLCAic2hlZXAiKSwKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKQoKcmVnIDwtIGdsbShhcy5mb3JtdWxhKHBhc3RlKCJjbGllbnQgfiIsIHBzbVZhcnMsIHNlcD0iIikpLCAKICAgICAgICAgICBmYW1pbHk9IGJpbm9taWFsKGxpbms9ImxvZ2l0IiksIGRhdGE9ZCkKc3VtbWFyeShyZWcpCQoKIyBzdW1tYXJpemUgcHJlZGljdGVkIHByb2JhYmlsaXRpZXMKcHIgPC0gZGF0YS5mcmFtZShwcl9zY29yZSA9IHByZWRpY3QocmVnLCB0eXBlPSdyZXNwb25zZScpLCB0cmVhdCA9IGQkY2xpZW50KQoKIyBncmFwaApwc21HcmFwaCA8LSBnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGRhdGE9c3Vic2V0KHByLCBwciR0cmVhdD09MSksIGFlcyh4ID0gcHJfc2NvcmUsIHk9Li5jb3VudC4uLCBmaWxsPWFzLmZhY3Rvcih0cmVhdCkpLCBiaW5zPTgwLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgIGdlb21faGlzdG9ncmFtKGRhdGE9c3Vic2V0KHByLCBwciR0cmVhdD09MCksIGFlcyh4PXByX3Njb3JlLCB5PS0uLmNvdW50Li4sIGZpbGw9YXMuZmFjdG9yKHRyZWF0KSksIGJpbnM9ODAsIHBvc2l0aW9uID0gImlkZW50aXR5IikgKwogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC0xNTAsMTUwKSkgKyAKICBsYWJzKHRpdGxlID0iUFNNIHNjb3JlIG92ZXJsYXAiLCB4ID0gIlBTTSBzY29yZSIsIHk9IkZhcm1lciBjb3VudCIsCiAgICAgICBmaWxsPSJUdWJ1cmEvQ29udHJvbCIpCgpwcmludChwc21HcmFwaCkKCnBkZihmaWxlPXBhc3RlKCJvdXRwdXQiLCAicndfYmFzZWxpbmVfcHNtX292ZXJsYXAucGRmIiwgc2VwPSIvIiksIGhlaWdodD04LjUsIHdpZHRoPTExKQpwcmludChwc21HcmFwaCkKZGV2Lm9mZigpCmBgYAoKKipJbnRlcnByZXRhdGlvbioqIFdlIGhhdmUgc29tZSBvdmVybGFwIGJ1dCBpdCdzIGNsZWFyIHRoYXQgVHVidXJhIGZhcm1lcnMgb2NjdXB5IGEgZGlmZmVyZW50IHJhbmdlIHRoYW4gdGhlIGlkZW50aWZpZWQgY29udHJvbCBmYXJtZXJzLiBMZXQncyBjb250aW51ZSB3aXRoIHRoZSBQU00gbWF0Y2hpbmcgcHJvY2VzcyBidXQgcmVzdHJpY3Qgb3Vyc2VsdmVzIHRvIFR1YnVyYSBhbmQgY29udHJvbCBmYXJtZXJzIHRoYXQgbWVldCBhIGNlcnRhaW4gUFNNIG1hdGNoaW5nIHJhZGl1cy4KCiMjIyBOb3RlcyBvbiBQU00gcHJvY2VzczoKV2UgaGF2ZSB0byBpbmRpY2F0ZSBhIHZhcmlhYmxlIGZvciBtYXRjaGluZy4gSSdtIGNob29zaW5nIHBIIGFzIHdlIGtub3cgaXQgdG8gYmUgYW4gaXNzdWUgaW4gbW9zdCBvZiBvdXIgb3BlcmF0aW5nIGFyZWFzIGFuZCBhZGRyZXNzaW5nIHNvaWwgYWNpZGl0eSBoYXMgbnVtZXJvdXMgcmVzaWR1YWwgYmVuZWZpdHMgdG8gc29pbCBoZWFsdGguIEknbGwgd2FudCB0byBkbyB0aGlzIGZvciBhbGwgc29pbCBvdXRjb21lcywgaG93ZXZlci4KCiogQXMgYSBuZXh0IHN0ZXAsIEknZCBsaWtlIHRvIGlkZW50aWZ5IHRoZSBtYXRjaGVkIHN1YnNldCBhbmQgdGhlbiByZWdyZXNzIFR1YnVyYSB0ZW51cmUgb24gc29pbCBvdXRjb21lcy4gRG9lcyB0aGF0IG1ha2Ugc2Vuc2U/CiogSSBjaG9zZSBhIGNhbGlwZXIgb2YgMC4yNS4gSSBzaG91bGQgcmV2aWV3IHRoaXMgcHJvY2VzcyB3aXRoIE1heWEgdG8gbWFrZSBjZXJ0YWluIEknbSBmb2xsb3dpbmcgYmVzdCBwcmFjdGljZS4gSSBzb3J0IG9mIGp1c3QgcHVsbGVkIHRoYXQgc2QgZmlndXJlIG91dCBvZiB0aGUgc2t5LgoqIEFuIGluaXRpYWwgY2hlY2sgb2YgdGhlIG1hdGNoIHF1YWxpdHkgdXNpbmcgYSBjb21tb24gbW9kZWwgaW5kaWNhdGVzIHRoYXQgdGhlIG1hdGNoZXMgYXJlIHBvb3IuCgoKYGBge3J9CiMgUFNNIHByZXAKdHIgPC0gY2JpbmQoZCRjbGllbnQpCnggPC0gZFssIHVubGlzdChzdHJzcGxpdChwc21WYXJzLCAiICsgIiwgZml4ZWQ9VCkpXQp5IDwtIHNvaWxWYXJzCgoKIyBQU00Kc2V0LnNlZWQoMjAxNjExMDIpCm0gPC0gbGFwcGx5KHksIGZ1bmN0aW9uKHJlc3BvbnNlKXsKCiAgc3VwcHJlc3NXYXJuaW5ncygKICBtb2QgPC0gTWF0Y2goWSA9IGRbLHJlc3BvbnNlXSwgVHIgPSB0ciwgWCA9IHJlZyRmaXR0ZWQsIHRpZXM9RkFMU0UsIHJlcGxhY2U9RkFMU0UsIGNhbGlwZXI9MC4yNSwgZXN0aW1hbmQgPSAiQVRFIikJCiAgKQogIG1hdGNoUmVzIDwtIE1hdGNoQmFsYW5jZSh0ciB+IGRbLHJlc3BvbnNlXSwgbWF0Y2gub3V0PW1vZCwgbmJvb3RzPTUwMCwgZGF0YT1kLCBwcmludC5sZXZlbCA9IDApCiAgcmV0dXJuKGxpc3QobW9kLCBtYXRjaFJlcykpCn0pCiNsYXBwbHkobSwgc3VtbWFyeSkKYGBgCgpOb3cgY2hlY2sgdGhlIG5haXZlIG1vZGVsIGFwcHJvYWNoIGZvciBQU00gYmFsYW5jZS4KCmBgYHtyfQptYXRjaFJlcyA8LSBkby5jYWxsKHJiaW5kLCBsYXBwbHkoMTpsZW5ndGgobSksIGZ1bmN0aW9uKG1vZGVsKXsKICB2YWwgPC0gYXMuZGF0YS5mcmFtZShjYmluZCgKICAgIHN0YW5kYXJkLmRpZmY9bVtbbW9kZWxdXVtbMl1dJEFmdGVyTWF0Y2hpbmdbWzFdXSRzZGlmZiwgCiAgICB2YXIucmF0aW8gPSBtW1ttb2RlbF1dW1syXV0kQWZ0ZXJNYXRjaGluZ1tbMV1dJHZhci5yYXRpbywKICAgIHNkaWZmLmFkaiA9IG1bW21vZGVsXV1bWzJdXSRBZnRlck1hdGNoaW5nW1sxXV0kc2RpZmYvMTAwKSkKICByZXR1cm4odmFsKQp9KSkKcm93bmFtZXMobWF0Y2hSZXMpIDwtIHkKYGBgCgpgYGB7ciByZXN1bHRzPSdhc2lzJ30KcHJpbnQoa2FibGUobWF0Y2hSZXMpKQpgYGAKCgoqKkludGVycHJldGF0aW9uKio6IFdlIHdhbnQgdG8gc2VlIHN0YW5kYXJkIG1lYW4gZGlmZmVyZW5jZXMgbGVzcyB0aGFuIHRoZSBhYnNvbHV0ZSB2YWx1ZSBvZiAwLjI1IChvciAwLjEgaWYgd2UncmUgYmVpbmcgY29uc2VydmF0aXZlKSBhbmQgdmFyaWFuY2UgcmF0aW9zIGNsb3NlIHRvIDEgYnV0IGNlcnRhaW5seSBiZXR3ZWVuIDAuNSBhbmQgMi4gCgpBY2NvcmRpbmcgdG8gdGhlIFtDUkFOIHN1bW1hcnldKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9NYXRjaGluZy9NYXRjaGluZy5wZGYpLCBzZGlmZiBpcyB0aGUgc3RhbmRhcmRpemVkIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgdHJlYXRtZW50IGFuZCBjb250cm9sIHVuaXRzICptdWx0aXBsaWVkIGJ5IDEwMCouIElmIEkgZGl2aWRlIGJ5IDEwMCwgdGhlIHZhbHVlcyBjb21lIG11Y2ggY2xvc2VyIHRvIHJlYXNvbmFibGUgdmFsdWUuCgojIyMgTGV0IHRoZSBQU00gbW9kZWxzIHZhcnkKVGhlIGNvbW1vbiBtb2RlbCBhcHByb2FjaCBkb2Vzbid0IHNlZW0gdG8gYmUgd29ya2luZyBmb3IgYW55IG9mIHRoZSB2YXJpYWJsZXMuIEknbSBnb2luZyB0byByZXdvcmsgdGhlIG1vZGVsaW5nIGFwcHJvYWNoIHNvIHdlIGNhbiBmaXQgZGlmZmVyZW50IG1vZGVscyBmb3IgZWFjaCBvdXRjb21lIHVwb24gd2hpY2ggd2UncmUgdHJ5aW5nIHRvIG1hdGNoLgoKYGBge3J9CmQkYWdlMiA8LSBkJGFnZV4yCmQkaGhzaXplX2FnZSA8LSBkJGhoc2l6ZSpkJGFnZQpkJGhoc2l6ZTIgPC0gZCRoaHNpemVeMgoKY29yZVZhcnMgPSBjKCJmZW1hbGUiLCAiYWdlIiwgImhoc2l6ZSIsICJvd24iLCAiYXMuZmFjdG9yKGNlbGwpIiwgImNvd3MiLCAiZ29hdHMiLCAiY2hpY2tlbnMiLCAicGlncyIsICJzaGVlcCIsICJibGFja19zb2lsIiwgInJlZF9zb2lsIiwgInNhbmR5X3NvaWwiKQoKcHNtTGlzdCA8LSBsaXN0KAogIGxpc3QodHIgPSAiY2xpZW50IiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9Im0zLkNhIiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibTMuTWciKSwKICBsaXN0KHRyID0gImNsaWVudCIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJwSCIpLAogIGxpc3QodHIgPSAiY2xpZW50IiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9IlRvdGFsLk4iKSwKICBsaXN0KHRyID0gImNsaWVudCIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJUb3RhbC5DIiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0iRXhBbCIpLAogIGxpc3QodHIgPSAiY2xpZW50IiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9Im5fc2Vhc29uX2ZlcnQiKSwKICBsaXN0KHRyID0gImNsaWVudCIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJuX3NlYXNvbl9jb21wb3N0IiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibl9zZWFzb25zX2xlZ18xIiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibl9zZWFzb25fZmFsbG93IiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibG9nRmVydCIpLAogIGxpc3QodHIgPSAiY2xpZW50IiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjKGNvcmVWYXJzLCAiYWdlMiIsCiAgICAgICAgICAgICAgICAgICAiaGhzaXplMiIpLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibl9zZWFzb25zX2xlZ18yIiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGMoY29yZVZhcnMsICJhZ2UyIiwKICAgICAgICAgICAgICAgICAgICJoaHNpemUyIiksCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJuX3NlYXNvbl9saW1lIiksCiAgbGlzdCh0ciA9ICJjbGllbnQiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGMoImZlbWFsZSIsICJhZ2UiLCAiaGhzaXplIiwgIm93biIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiY293cyIsICJnb2F0cyIsICJjaGlja2VucyIsICJwaWdzIiwgInNoZWVwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJhcy5mYWN0b3IoZGlzdHJpY3QpIiwgImFnZTIiLCAiaGhzaXplMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiaGhzaXplX2FnZSIpLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0iZmVydDEuYXJlIiksCiAgIyBsaXN0KHRyID0gImNsaWVudCIsCiAgIyAgICAgIHBzbVZhcnMgPSBwYXN0ZShjKCJmZW1hbGUiLCAiYWdlIiwgImhoc2l6ZSIsImFzLmZhY3RvcihkaXN0cmljdCkiLAogICMgICAgICAgICAgICAgICAgICAiY293cyIsICJnb2F0cyIsICJjaGlja2VucyIsICJwaWdzIiwgInNoZWVwIiwiYWdlMiIsICJoaHNpemUyIiwKICAjICAgICAgICAgICAgICAgICAgICAgICAgImhoc2l6ZV9hZ2UiKSwKICAjICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICMgICAgICB5PSJmZXJ0Mi5hcmUiKSwKICBsaXN0KHRyID0gImNsaWVudCIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoYygiZmVtYWxlIiwgImFnZSIsICJoaHNpemUiLCJhcy5mYWN0b3IoZGlzdHJpY3QpIiwKICAgICAgICAgICAgICAgICAgICJjb3dzIiwgImdvYXRzIiwgImNoaWNrZW5zIiwgInBpZ3MiLCAic2hlZXAiLCJhZ2UyIiwgImhoc2l6ZTIiLAogICAgICAgICAgICAgICAgICAgICAgICAgImhoc2l6ZV9hZ2UiKSwKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9ImNvbXBvc3QuYXJlIikKICAKKQoKIyBmZXJ0aWxpemVyIGFwcGxpY2F0aW9uIGluIDE1QiBoYXMgbWlzc2luZyB2YWx1ZXMgYXQgdGhlIGNlbGwgbGV2ZWwgc28gaSBpbmNsdWRlCiMgYSBkaXN0cmljdCBsZXZlbCBjb250cm9sIGluc3RlYWQuIFdlIGRvbid0IGdldCBhIGdvb2QgZml0IHdpdGggdGhlIGN1cnJlbnQgbW9kZWwuCiMgYWRqdXN0IQojIGFnZ3JlZ2F0ZShkJGZlcnQxLmFyZSwgYnk9bGlzdChkJGNsaWVudCwgZCRjZWxsKSwgRlVOPW1lYW4sIG5hLnJtPVQpCiMgbGltZS5hcmUgaXMgYWxzbyBiZWluZyBmaW5pY2t5CgoKCiMgUFNNCnNldC5zZWVkKDIwMTYxMTAyKQptIDwtIGxhcHBseShwc21MaXN0LCBmdW5jdGlvbihsaXN0SW5wdXQpewoKICBuYUNoZWNrIDwtIHVubGlzdChzdHJzcGxpdChnc3ViKCJcXCsiLCAiIiwgYXMudmVjdG9yKGxpc3RJbnB1dCRwc21WYXJzKSksIiAiLCBmaXhlZD1UUlVFKSkKICBuYUNoZWNrIDwtIG5hQ2hlY2tbLXdoaWNoKG5hQ2hlY2s9PSIiKV0KICAKICAjIGtlZXAgY29tcGxldGUgY2FzZXMgb2Ygb3V0Y29tZSB2YXJpYWJsZQogIGsgPC0gZFtjb21wbGV0ZS5jYXNlcyhkWyxsaXN0SW5wdXQkeV0pLF0KICBrIDwtIGtbY29tcGxldGUuY2FzZXMoa1ssbGlzdElucHV0JHldKSxdCiAgCiAgIyBydW4gZ2xtIHJlZ3Jlc3Npb246CiAgcmVnIDwtIGdsbShhcy5mb3JtdWxhKHBhc3RlKGxpc3RJbnB1dCR0ciwgIn4iLCBsaXN0SW5wdXQkcHNtVmFycywgc2VwPSIiKSksICBmYW1pbHk9IGJpbm9taWFsKGxpbms9ImxvZ2l0IiksIGRhdGE9aykKICAKICBzdXBwcmVzc1dhcm5pbmdzKAogIG1vZCA8LSBNYXRjaChZID0ga1ssbGlzdElucHV0JHldLCBUciA9IGtbLGxpc3RJbnB1dCR0cl0sIFggPSByZWckZml0dGVkLCB0aWVzPUZBTFNFLCByZXBsYWNlPUZBTFNFLCBjYWxpcGVyPTAuMjUsIGVzdGltYW5kID0gIkFURSIpCQogICkKICBtYXRjaFJlcyA8LSBNYXRjaEJhbGFuY2Uoa1ssbGlzdElucHV0JHRyXSB+IGtbLGxpc3RJbnB1dCR5XSwgbWF0Y2gub3V0PW1vZCwgbmJvb3RzPTUwMCwgZGF0YT1rLCBwcmludC5sZXZlbCA9IDApCiAgI3ByaW50KGxpc3RJbnB1dCR5KQogIHJldHVybihsaXN0KG1vZCwgbWF0Y2hSZXMpKQogIAp9KQoKYGBgCgpUaGUgbW9kZWxzIGNhbiBub3cgdmFyeSBieSBvdXRjb21lLiBMZXQncyBzZWUgaWYgd2UgY2FuIGltcHJvdmUgb3VyIHJlc3VsdHMuCgpgYGB7cn0KbWF0Y2hSZXMgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KDE6bGVuZ3RoKG0pLCBmdW5jdGlvbihtb2RlbCl7CiAgdmFsIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoCiAgICBzdGFuZGFyZC5kaWZmPW1bW21vZGVsXV1bWzJdXSRBZnRlck1hdGNoaW5nW1sxXV0kc2RpZmYsIAogICAgdmFyLnJhdGlvID0gbVtbbW9kZWxdXVtbMl1dJEFmdGVyTWF0Y2hpbmdbWzFdXSR2YXIucmF0aW8sCiAgICBzZGlmZi5hZGogPSBtW1ttb2RlbF1dW1syXV0kQWZ0ZXJNYXRjaGluZ1tbMV1dJHNkaWZmLzEwMCkpCiAgcmV0dXJuKHZhbCkKfSkpCgpuYW1lc0lucHV0IDwtIE5VTEwKZm9yKGkgaW4gMTpsZW5ndGgocHNtTGlzdCkpewogIG5hbWVzSW5wdXRbaV0gPC0gcHNtTGlzdFtbaV1dJHkKfQpyb3duYW1lcyhtYXRjaFJlcykgPC0gbmFtZXNJbnB1dApgYGAKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZShtYXRjaFJlcykpCmBgYAoKKipJbnRlcnByZXRhdGlvbioqIElmIEkgZGl2aWRlIHRoZSBzdGFuZGFyZGl6ZWQgbWVhbiBkaWZmZXJlbmNlcyBieSAxMDAsIHdlIG1lZXQgdGhlIGJhbGFuY2UgY3JpdGVyaWEgb2YgdGhlIHN0YW5kYXJkaXplZCBtZWFuIGRpZmZlcmVuY2UgYmVpbmcgY2xvc2UgdG8gMCBhbmQgdGhlIHZhcmlhbmNlIGJlaW5nIGNsb3NlIHRvIDEuIExldCdzIHByaW50IG91dCB0aGUgbW9kZWwgcmVzdWx0cyB0byBzZWUgaG93IFR1YnVyYSBhbmQgY29udHJvbCBmYXJtZXJzIGNvbXBhcmUgb24ga2V5IHNvaWwgbWV0ZXJpY3MuIFRoZXNlIHJlc3VsdHMgc2hvdWxkIHN1cGVyY2VkZSB0aGUgbmFpdmUgYmFsYW5jZSB0YWJsZXMgcHJlc2VudGVkIGFib3ZlLgoKV2UgYWNoaWV2ZSBhY2NlcHRhYmxlIGJhbGFuY2UgZm9yIHRoZSBzb2lsIGF0dHJpYnV0ZXMgYnV0IHdlIGRvbid0IGZvciBzZWFzb25zIG9mIGZlcnRpbGl6ZXIgdXNlLiBUaGlzIGlzIGlzbid0IGVudGlyZWx5IHVuZXhwZWN0ZWQgZ2l2ZW4gdGhhdCBUdWJ1cmEncyBwcmltYXJ5IHNlcnZpY2UgaXMgcHJvdmlkaW5nIGZlcnRpbGl6ZXIgaW5wdXRzIGFuZCB0cmFpbmluZy4KCmBgYHtyfQpjb2VmVGFibGUgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KDE6bGVuZ3RoKG0pLCBmdW5jdGlvbihtb2RlbCl7CiAgYmV0YSA9IHJvdW5kKG1bW21vZGVsXV1bWzFdXSRlc3Qubm9hZGosMykKICBtZWFuLlRyID0gcm91bmQobVtbbW9kZWxdXVtbMl1dJEFmdGVyTWF0Y2hpbmdbWzFdXVtbM11dLCAyKQogIG1lYW4uQ28gPSByb3VuZChtW1ttb2RlbF1dW1syXV0kQWZ0ZXJNYXRjaGluZ1tbMV1dW1s0XV0sIDIpCiAgcHZhbCA9IG1bW21vZGVsXV1bWzJdXSRBZnRlck1hdGNoaW5nW1sxXV1bWzEwXV1bWzNdXSAjIHAudmFsdWUKICAjcHZhbCA9ICgxIC0gcG5vcm0oYWJzKG1bW21vZGVsXV1bWzFdXSRlc3QvbVtbbW9kZWxdXVtbMV1dJHNlLnN0YW5kYXJkKSkpICogMgogIHB2YWwgPSBpZmVsc2UocHZhbCA8IDAuMDAxLCAiMC4wMDEiLCByb3VuZChwdmFsLCAzKSkKICAKICByZXMgPSBkYXRhLmZyYW1lKGJldGEsIG1lYW4uVHIsIG1lYW4uQ28sIHB2YWwpCiAgcmV0dXJuKHJlcykKfSkpCnJvdy5uYW1lcyhjb2VmVGFibGUpIDwtIG5hbWVzSW5wdXQKY29lZlRhYmxlJHB2YWwuYWRqIDwtIHJvdW5kKHAuYWRqdXN0KGNvZWZUYWJsZSRwdmFsLCBtZXRob2Q9ImZkciIpLDMpCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnByaW50KGthYmxlKGNvZWZUYWJsZSkpCmBgYAoKSSdtIGNvbXBhcmluZyBzZWFzb24gMSBjbGllbnRzIHRvIGNsaWVudHMgd2l0aCBtb3JlIHRlbnVyZSBpbiB0aGUgUFNNIG1hdGNoaW5nIGJ1dCBJIGFsc28gbmVlZCB0aGlzIHZhcmlhYmxlIGZvciB0aGUgdGhyZXNob2xkIGNhbGN1bGF0aW9ucwoKYGBge3J9CiMgMCBpcyBuZXcgY2xpZW50LCAxIGlzIHJldHVybmluZyBjbGllbnQKZCR0ZW51cmUudGllcnMgPC0gaWZlbHNlKGQkY2xpZW50PT0xICYgZCR0b3RhbC5zZWFzb25zPT0xLCAwLAogICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGQkY2xpZW50PT0xICYgZCR0b3RhbC5zZWFzb25zPjEsIDEsIE5BKSkKYGBgCgojIyBDYWxjdWxhdGUgZmFybWVycyB1bmRlciBzb2lsIGhlYWx0aCB0aHJlc2hvbGRzIC0gdGFibGUgMQoKYGBge3J9CnRocmVzaCA8LSBkICU+JSBncm91cF9ieShjbGllbnQpICU+JSBkcGx5cjo6c3VtbWFyaXplKAogIGNvdW50ID0gbigpLAogIHBoID0gc3VtKHBIPDUuOCksCiAgY2FyYm9uID0gc3VtKFRvdGFsLkMgPCAyKSwKICBuaXRyb2dlbiA9IHN1bShUb3RhbC5OIDwgMC4xKSwKICBjYWxjaXVtID0gc3VtKG0zLkNhIDwgMTA1NiksCiAgbWFnbmVzaXVtID0gc3VtKG0zLk1nIDwgMTQ4KQogICNhbHVtaW51bSA9IHN1bShFeEFsKQopICU+JSBtdXRhdGUoCiAgdW5kZXIucGggPSBwYXN0ZShwYXN0ZShyb3VuZChwaC9jb3VudCw0KSoxMDAsICIlIiwgc2VwPSIiKSwgIiAoIiwgcGgsICIpIiwgc2VwPSIiKSwKICB1bmRlci5jYXJib24gPSBwYXN0ZShwYXN0ZShyb3VuZChjYXJib24vY291bnQsNCkqMTAwLCIlIiwgc2VwPSIiKSwgIiAoIiwgY2FyYm9uLCAiKSIsIHNlcD0iIiksCiAgdW5kZXIubml0cm9nZW4gPSBwYXN0ZShwYXN0ZShyb3VuZChuaXRyb2dlbi9jb3VudCw0KSoxMDAsICIlIiwgc2VwPSIiKSwgIiAoIiwgbml0cm9nZW4sICIpIiwgc2VwPSIiKSwKICB1bmRlci5jYWxjaXVtID0gcGFzdGUocGFzdGUocm91bmQoY2FsY2l1bS9jb3VudCw0KSoxMDAsICIlIiwgc2VwPSIiKSwgIiAoIiwgY2FsY2l1bSwgIikiLCBzZXA9IiIpLAogIHVuZGVyLm1hZyA9IHBhc3RlKHBhc3RlKHJvdW5kKG1hZ25lc2l1bS9jb3VudCw0KSoxMDAsIiUiLCBzZXA9IiIpLCAiICgiLCBtYWduZXNpdW0sICIpIiwgc2VwPSIiKQopICU+JSBhcy5kYXRhLmZyYW1lKCkgCgp0aHJlc2ggPC0gdGhyZXNoWywgYygiY2xpZW50IiwgbmFtZXModGhyZXNoKVtncmVwKCJ1bmRlciIsIG5hbWVzKHRocmVzaCkpXSldCnRocmVzaCA8LSB0KHRocmVzaCkKY29sbmFtZXModGhyZXNoKSA9IHRocmVzaFsxLCBdICMgdGhlIGZpcnN0IHJvdyB3aWxsIGJlIHRoZSBoZWFkZXIKY29sbmFtZXModGhyZXNoKSA9IGMoIm5vbi1jbGllbnQiLCAiY2xpZW50IikKdGhyZXNoID0gdGhyZXNoWy0xLCBdCgp3cml0ZS5jc3YodGhyZXNoLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAidGFibGUxX3J3X3RocmVzaG9sZHMuY3N2Iiwgc2VwID0gIi8iKSwgcm93Lm5hbWVzID0gVCkKYGBgCgpOZXcgY2xpZW50cyB2cy4gcmV0dXJuaW5nIGNsaWVudHMgc3VtbWFyaWVzOgoKYGBge3J9Cm5ld09sZCA8LSBkICU+JSBmaWx0ZXIoIWlzLm5hKHRlbnVyZS50aWVycykpICU+JSBncm91cF9ieSh0ZW51cmUudGllcnMpICU+JSAKICBzdW1tYXJpc2UoCiAgcGggPSByb3VuZChtZWFuKHBILCBuYS5ybT1UKSwzKSwKICBjYXJib24gPSByb3VuZChtZWFuKFRvdGFsLkMsIG5hLnJtPVQpLDMpLAogIG5pdHJvZ2VuID0gcm91bmQobWVhbihUb3RhbC5OLCBuYS5ybT1UKSwzKSwKICBjYWxjaXVtID0gcm91bmQobWVhbihtMy5DYSwgbmEucm09VCksMyksCiAgbWFnbmVzaXVtID0gcm91bmQobWVhbihtMy5NZywgbmEucm09VCksMykKKSAlPiUgdW5ncm91cCgpIAoKbmV3T2xkIDwtIGFzLmRhdGEuZnJhbWUodChuZXdPbGQpKQpjb2xuYW1lcyhuZXdPbGQpID0gbmV3T2xkWzEsIF0gIyB0aGUgZmlyc3Qgcm93IHdpbGwgYmUgdGhlIGhlYWRlcgpjb2xuYW1lcyhuZXdPbGQpID0gYygiTmV3IENsaWVudCIsICJSZXR1cm5pbmcgQ2xpZW50IikKbmV3T2xkID0gbmV3T2xkWy0xLCBdCgojd3JpdGUuY3N2KG5ld09sZCwgZmlsZT1wYXN0ZShvZCwgInJ3IHRhYmxlMSBuZXcgb2xkIHZhbHVlcy5jc3YiLCBzZXAgPSAiLyIpLCByb3cubmFtZXMgPSBUKQpgYGAKCgpOZXcgQ2xpZW50cyB2cy4gcmV0dXJuaW5nIGNsaWVudHMgdGhyZXNob2xkcwoKYGBge3J9CnRocmVzaC50IDwtIGQgJT4lIGZpbHRlcighaXMubmEodGVudXJlLnRpZXJzKSkgJT4lCiAgZ3JvdXBfYnkodGVudXJlLnRpZXJzKSAlPiUgZHBseXI6OnN1bW1hcml6ZSgKICBjb3VudCA9IG4oKSwKICBwaCA9IHN1bShwSDw1LjgpLAogIGNhcmJvbiA9IHN1bShUb3RhbC5DIDwgMiksCiAgbml0cm9nZW4gPSBzdW0oVG90YWwuTiA8IDAuMSksCiAgY2FsY2l1bSA9IHN1bShtMy5DYSA8IDEwNTYpLAogIG1hZ25lc2l1bSA9IHN1bShtMy5NZyA8IDE0OCkKICAjYWx1bWludW0gPSBzdW0oRXhBbCkKKSAlPiUgbXV0YXRlKAogIHVuZGVyLnBoID0gcGFzdGUocGFzdGUocm91bmQocGgvY291bnQsNCkqMTAwLCAiJSIsIHNlcD0iIiksICIgKCIsIHBoLCAiKSIsIHNlcD0iIiksCiAgdW5kZXIuY2FyYm9uID0gcGFzdGUocGFzdGUocm91bmQoY2FyYm9uL2NvdW50LDQpKjEwMCwiJSIsIHNlcD0iIiksICIgKCIsIGNhcmJvbiwgIikiLCBzZXA9IiIpLAogIHVuZGVyLm5pdHJvZ2VuID0gcGFzdGUocGFzdGUocm91bmQobml0cm9nZW4vY291bnQsNCkqMTAwLCAiJSIsIHNlcD0iIiksICIgKCIsIG5pdHJvZ2VuLCAiKSIsIHNlcD0iIiksCiAgdW5kZXIuY2FsY2l1bSA9IHBhc3RlKHBhc3RlKHJvdW5kKGNhbGNpdW0vY291bnQsNCkqMTAwLCAiJSIsIHNlcD0iIiksICIgKCIsIGNhbGNpdW0sICIpIiwgc2VwPSIiKSwKICB1bmRlci5tYWcgPSBwYXN0ZShwYXN0ZShyb3VuZChtYWduZXNpdW0vY291bnQsNCkqMTAwLCIlIiwgc2VwPSIiKSwgIiAoIiwgbWFnbmVzaXVtLCAiKSIsIHNlcD0iIikKKSAlPiUgYXMuZGF0YS5mcmFtZSgpIAoKdGhyZXNoLnQgPC0gdGhyZXNoLnRbLCBjKCJ0ZW51cmUudGllcnMiLCBuYW1lcyh0aHJlc2gudClbZ3JlcCgidW5kZXIiLCBuYW1lcyh0aHJlc2gudCkpXSApXQp0aHJlc2gudCA8LSB0KHRocmVzaC50KQpjb2xuYW1lcyh0aHJlc2gudCkgPSB0aHJlc2gudFsxLCBdICMgdGhlIGZpcnN0IHJvdyB3aWxsIGJlIHRoZSBoZWFkZXIKY29sbmFtZXModGhyZXNoLnQpID0gYygibmV3IGNsaWVudCB0aG9sZCIsICJyZXR1cm5pbmcgY2xpZW50IHRob2xkIikKdGhyZXNoLnQgPSB0aHJlc2gudFstMSwgXQoKIyBjb21iaW5lIG5ld09sZCBzdW1tYXJpZXMgYW5kIHBlcmNlbnRzIGJlbG93IHBlcmNlbnRpbGVzCnRocmVzaC50IDwtIGNiaW5kKG5ld09sZCwgdGhyZXNoLnQpCgp3cml0ZS5jc3YodGhyZXNoLnQsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJ0YWJsZTFfcndfdGhyZXNob2xkc190ZW51cmUuY3N2Iiwgc2VwID0gIi8iKSwgcm93Lm5hbWVzID0gVCkKYGBgCgojIyBUYWJsZSAxOjMgY29lZmZpY2llbnRzCgpgYGB7cn0KIwp3cml0ZS5jc3YoY29lZlRhYmxlLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicHNtIGNvZWZmaWNpZW50cy5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzID0gVCkKCiMgc29ydCBieSB0aGUgb3JkZXIgRXJpYyB3YW50cwp0MW9yZGVyIDwtIGMoInBIIiwgIlRvdGFsLkMiLCAiVG90YWwuTiIsICJtMy5DYSIsICJtMy5NZyIsICJFeEFsIikKdGFibGUxdmFycyA8LSBwYXN0ZSh0MW9yZGVyLCBjb2xsYXBzZSA9ICJ8IikKdGFibGUxIDwtIGNvZWZUYWJsZVtncmVwKHRhYmxlMXZhcnMsIHJvd25hbWVzKGNvZWZUYWJsZSkpLCBdCnRhYmxlMSA8LSB0YWJsZTFbb3JkZXIobWF0Y2gocm93bmFtZXModGFibGUxKSwgdDFvcmRlcikpLF0KCndyaXRlLmNzdih0YWJsZTEsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJwc20gY29lZmZpY2llbnRzIG9yZGVyZWQgZm9yIEVTLmNzdiIsIHNlcCA9ICIvIikpCgojIDExLzE3IGFkZGVkIGxpbWUKdDJvcmRlciA8LSBjKCJuX3NlYXNvbl9mZXJ0IiwgIm5fc2Vhc29uX2NvbXBvc3QiLCAibl9zZWFzb25fbGltZSIsICJuX3NlYXNvbl9mYWxsb3ciLCAibl9zZWFzb25zX2xlZ18xIiwgIm5fc2Vhc29uc19sZWdfMiIpCnRhYmxlMnZhcnMgPC0gcGFzdGUodDJvcmRlciwgY29sbGFwc2UgPSAifCIpCnRhYmxlMiA8LSBjb2VmVGFibGVbZ3JlcCh0YWJsZTJ2YXJzLCByb3duYW1lcyhjb2VmVGFibGUpKSwgXQp0YWJsZTIgPC0gdGFibGUyW29yZGVyKG1hdGNoKHJvd25hbWVzKHRhYmxlMiksIHQyb3JkZXIpKSxdCgp3cml0ZS5jc3YodGFibGUyLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicHNtIGNvZWZmaWNpZW50cyBvcmRlcmVkIGZvciBFU19hZ3ByYWMuY3N2Iiwgc2VwID0gIi8iKSkKCiN0YWJsZSAzCnQzb3JkZXIgPC0gYygiZmVydDEuYXJlIiwgImNvbXBvc3QuYXJlIikKdGFibGUzdmFycyA8LSBwYXN0ZSh0M29yZGVyLCBjb2xsYXBzZSA9ICJ8IikKdGFibGUzIDwtIGNvZWZUYWJsZVtncmVwKHRhYmxlM3ZhcnMsIHJvd25hbWVzKGNvZWZUYWJsZSkpLCBdCnRhYmxlMyA8LSB0YWJsZTNbb3JkZXIobWF0Y2gocm93bmFtZXModGFibGUzKSwgdDNvcmRlcikpLF0Kd3JpdGUuY3N2KHRhYmxlMywgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInBzbSBjb2VmZmljaWVudHMgdGFibGUzLmNzdiIsIHNlcCA9ICIvIikpCmBgYAoKKipJbnRlcnByZXRhdGlvbioqOiBQcm9wZW5zaXR5IHNjb3JlIG1hdGNoaW5nIGdpdmVzIHVzIGEgY29tcGFyYWJsZSB0cmVhdG1lbnQgYW5kIGNvbnRyb2wgZ3JvdXAuIFRoZSB0YWJsZSBhYm92ZSBzaG93cyB0aGF0IGFmdGVyIG1hdGNoaW5nIG9uIHRob3NlIGNoYXJhY3RlcmlzdGljcywgdGhlcmUgYXJlIGVmZmVjdGl2ZWx5IG5vIGRpZmZlcmVuY2VzIGJldHdlZW4gT25lIEFjcmUgRnVuZCBmYXJtZXIgYW5kIFR1YnVyYSBmYXJtZXJzIG9uIHNvaWwgYXR0cmlidXRlcy4gVGhlIHVuYWRqdXN0ZWQgcC12YWx1ZXMgc2hvdyAxQUYgZmFybWVycyB0byBoYXZlIHNsb3cgbGV2ZWxzIG9mIHNvaWwgbml0cm9nZW4gYnV0IHRoaXMgZmluZGluZyBkaXNhcHBlYXJzIGlmIHdlIGFjY291bnQgZm9yIHJ1bm5pbmcgbXVsdGlwbGUgbWF0Y2hpbmcgbW9kZWxzLgoKV2hlbiB3ZSBleHBhbmQgdGhlIG91dGNvbWUgdmFyaWFibGUgc2V0IHRvIGluY2x1ZGUgcHJhY3RpY2UgdmFyaWFibGVzLCB3ZSBmaXJzdCBubyBsb25nZXIgZ2V0IGEgZ29vZCBwcm9wZW5zaXR5IHNjb3JlIG1hdGNoIGZvciBhbGwgdmFyaWFibGVzLiAKCiogVHVidXJhIHBhcnRpY2lwYXRpb24gc28gc3Ryb25nbHkgb3ZlcmxhcHMgd2l0aCBmZXJ0aWxpemVyIHVzZSB0aGF0IHRoZSBjb21wYXJpc29uIGdyb3VwIGlzIG5vIGxvbmdlciBjb21wYXJhYmxlLiBUaGUgcmVzdWx0aW5nIG1vZGVsIHNob3dzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBzZWFzb25zIG9mIGZlcnRpbGl6ZXIgdXNlLgoqIFR1YnVyYSBmYXJtZXJzIGhhdmUgdXNlZCBjb21wb3N0IGZvciAwLjMgbW9yZSBzZWFzb25zIHRoYW4gbm9uLVR1YnVyYSBmYXJtZXJzLiBXZSBkb24ndCBvYnNlcnZlIGEgZGlmZmVyZW5jZSBpbiBOIG9yIEMgbGV2ZWxzIGluIHRoZSBzb2lsIGF0dHJpYnV0ZXMgaG93ZXZlci4KKiBXZSBkb24ndCBzZWUgYSBkaWZmZXJlbmNlIGJldHdlZW4gVHVidXJhIGFuZCBub24tVHVidXJhIGZhcm1lcnMgb24gc2Vhc29ucyBvZiBsZWd1bWVzIGJlaW5nIHRoZSBwcmltYXJ5IGNyb3AgaW4gdGhlIHN0dWR5IHBsb3QuCgojIyBCcmVhayBvdXQgZmlyc3Qgc2Vhc29uIGNsaWVudHMgZnJvbSBtb3JlIHRlbnVyZWQgLSBmb3IgYXBwZW5kaXg/CgpUaGlzIHJlcXVpcmVzIHVzIHRvIHJlLXJ1biB0aGUgUFNNIG1hdGNoaW5nIHByb2Nlc3MsIGNvbmZpcm0gdGhhdCBvdXIgbW9kZWxzIGFyZSBzb3VuZCBhbmQgdGhlbiBvdXRwdXQgdGhlIHJlc3VsdHMgYWxhIHRhYmxlIDIgYWdhaW4uIAoKCmBgYHtyfQpwc21MaXN0IDwtIGxpc3QoCiAgbGlzdCh0ciA9ICJ0ZW51cmUudGllcnMiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibTMuQ2EiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJtMy5NZyIpLAogIGxpc3QodHIgPSAidGVudXJlLnRpZXJzIiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9InBIIiksCiAgbGlzdCh0ciA9ICJ0ZW51cmUudGllcnMiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0iVG90YWwuTiIpLAogIGxpc3QodHIgPSAidGVudXJlLnRpZXJzIiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9IlRvdGFsLkMiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJFeEFsIiksCiAgbGlzdCh0ciA9ICJ0ZW51cmUudGllcnMiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGNvcmVWYXJzLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0ibl9zZWFzb25fZmVydCIpLAogIGxpc3QodHIgPSAidGVudXJlLnRpZXJzIiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjb3JlVmFycywKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9Im5fc2Vhc29uX2NvbXBvc3QiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJuX3NlYXNvbnNfbGVnXzEiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJuX3NlYXNvbl9mYWxsb3ciKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoY29yZVZhcnMsCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJsb2dGZXJ0IiksCiAgbGlzdCh0ciA9ICJ0ZW51cmUudGllcnMiLAogICAgICAgcHNtVmFycyA9IHBhc3RlKGMoY29yZVZhcnMsICJhZ2UyIiwKICAgICAgICAgICAgICAgICAgICJoaHNpemUyIiksCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJuX3NlYXNvbnNfbGVnXzIiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoYyhjb3JlVmFycywgImFnZTIiLAogICAgICAgICAgICAgICAgICAgImhoc2l6ZTIiKSwKICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlPSIgKyAiKSwKICAgICAgIHk9Im5fc2Vhc29uX2xpbWUiKSwKICBsaXN0KHRyID0gInRlbnVyZS50aWVycyIsCiAgICAgICBwc21WYXJzID0gcGFzdGUoYygiZmVtYWxlIiwgImFnZSIsICJoaHNpemUiLCAib3duIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJjb3dzIiwgImdvYXRzIiwgImNoaWNrZW5zIiwgInBpZ3MiLCAic2hlZXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgImFzLmZhY3RvcihkaXN0cmljdCkiLCAiYWdlMiIsICJoaHNpemUyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJoaHNpemVfYWdlIiksCiAgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgICAgICB5PSJmZXJ0MS5hcmUiKSwKICAjIGxpc3QodHIgPSAidGVudXJlLnRpZXJzIiwKICAjICAgICAgcHNtVmFycyA9IHBhc3RlKGMoImZlbWFsZSIsICJhZ2UiLCAiaGhzaXplIiwiYXMuZmFjdG9yKGRpc3RyaWN0KSIsCiAgIyAgICAgICAgICAgICAgICAgICJjb3dzIiwgImdvYXRzIiwgImNoaWNrZW5zIiwgInBpZ3MiLCAic2hlZXAiLCJhZ2UyIiwgImhoc2l6ZTIiLAogICMgICAgICAgICAgICAgICAgICAgICAgICAiaGhzaXplX2FnZSIpLAogICMgICAgICAgICAgICAgICAgICBjb2xsYXBzZT0iICsgIiksCiAgIyAgICAgIHk9ImZlcnQyLmFyZSIpLAogIGxpc3QodHIgPSAidGVudXJlLnRpZXJzIiwKICAgICAgIHBzbVZhcnMgPSBwYXN0ZShjKCJmZW1hbGUiLCAiYWdlIiwgImhoc2l6ZSIsImFzLmZhY3RvcihkaXN0cmljdCkiLAogICAgICAgICAgICAgICAgICAgImNvd3MiLCAiZ29hdHMiLCAiY2hpY2tlbnMiLCAicGlncyIsICJzaGVlcCIsImFnZTIiLCAiaGhzaXplMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiaGhzaXplX2FnZSIpLAogICAgICAgICAgICAgICAgICAgY29sbGFwc2U9IiArICIpLAogICAgICAgeT0iY29tcG9zdC5hcmUiKQogIAopCgojIGZlcnRpbGl6ZXIgYXBwbGljYXRpb24gaW4gMTVCIGhhcyBtaXNzaW5nIHZhbHVlcyBhdCB0aGUgY2VsbCBsZXZlbCBzbyBpIGluY2x1ZGUKIyBhIGRpc3RyaWN0IGxldmVsIGNvbnRyb2wgaW5zdGVhZC4gV2UgZG9uJ3QgZ2V0IGEgZ29vZCBmaXQgd2l0aCB0aGUgY3VycmVudCBtb2RlbC4KIyBhZGp1c3QhCiMgYWdncmVnYXRlKGQkZmVydDEuYXJlLCBieT1saXN0KGQkY2xpZW50LCBkJGNlbGwpLCBGVU49bWVhbiwgbmEucm09VCkKIyBsaW1lLmFyZSBpcyBhbHNvIGJlaW5nIGZpbmlja3kKCgoKIyBQU00Kc2V0LnNlZWQoMjAxNjExMDIpCm1hcHBlbmQgPC0gbGFwcGx5KHBzbUxpc3QsIGZ1bmN0aW9uKGxpc3RJbnB1dCl7CgogIG5hQ2hlY2sgPC0gdW5saXN0KHN0cnNwbGl0KGdzdWIoIlxcKyIsICIiLCBhcy52ZWN0b3IobGlzdElucHV0JHBzbVZhcnMpKSwiICIsIGZpeGVkPVRSVUUpKQogIG5hQ2hlY2sgPC0gbmFDaGVja1std2hpY2gobmFDaGVjaz09IiIpXQogIAogICMga2VlcCBjb21wbGV0ZSBjYXNlcyBvZiBvdXRjb21lIHZhcmlhYmxlCiAgayA8LSBkW2NvbXBsZXRlLmNhc2VzKGRbLGxpc3RJbnB1dCR5XSksXQogIGsgPC0ga1tjb21wbGV0ZS5jYXNlcyhrWyxsaXN0SW5wdXQkeV0pLF0KICBrIDwtIGtbY29tcGxldGUuY2FzZXMoa1ssbGlzdElucHV0JHRyXSksXQogIAogICMgcnVuIGdsbSByZWdyZXNzaW9uOgogIHJlZyA8LSBnbG0oYXMuZm9ybXVsYShwYXN0ZShsaXN0SW5wdXQkdHIsICJ+IiwgbGlzdElucHV0JHBzbVZhcnMsIHNlcD0iIikpLCAgZmFtaWx5PSBiaW5vbWlhbChsaW5rPSJsb2dpdCIpLCBkYXRhPWspCiAgCiAgc3VwcHJlc3NXYXJuaW5ncygKICBtb2QgPC0gTWF0Y2goWSA9IGtbLGxpc3RJbnB1dCR5XSwgVHIgPSBrWyxsaXN0SW5wdXQkdHJdLCBYID0gcmVnJGZpdHRlZCwgdGllcz1GQUxTRSwgcmVwbGFjZT1GQUxTRSwgY2FsaXBlcj0wLjI1LCBlc3RpbWFuZCA9ICJBVEUiKQkKICApCiAgbWF0Y2hSZXMgPC0gTWF0Y2hCYWxhbmNlKGtbLGxpc3RJbnB1dCR0cl0gfiBrWyxsaXN0SW5wdXQkeV0sIG1hdGNoLm91dD1tb2QsIG5ib290cz01MDAsIGRhdGE9aywgcHJpbnQubGV2ZWwgPSAwKQogICNwcmludChsaXN0SW5wdXQkeSkKICByZXR1cm4obGlzdChtb2QsIG1hdGNoUmVzKSkKICAKfSkKCm1hdGNoUmVzIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseSgxOmxlbmd0aChtYXBwZW5kKSwgZnVuY3Rpb24obW9kZWwpewogIHZhbCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKAogICAgc3RhbmRhcmQuZGlmZj1tW1ttb2RlbF1dW1syXV0kQWZ0ZXJNYXRjaGluZ1tbMV1dJHNkaWZmLCAKICAgIHZhci5yYXRpbyA9IG1bW21vZGVsXV1bWzJdXSRBZnRlck1hdGNoaW5nW1sxXV0kdmFyLnJhdGlvLAogICAgc2RpZmYuYWRqID0gbVtbbW9kZWxdXVtbMl1dJEFmdGVyTWF0Y2hpbmdbWzFdXSRzZGlmZi8xMDApKQogIHJldHVybih2YWwpCn0pKQoKbmFtZXNJbnB1dCA8LSBOVUxMCmZvcihpIGluIDE6bGVuZ3RoKHBzbUxpc3QpKXsKICBuYW1lc0lucHV0W2ldIDwtIHBzbUxpc3RbW2ldXSR5Cn0Kcm93bmFtZXMobWF0Y2hSZXMpIDwtIG5hbWVzSW5wdXQKCmBgYAoKYGBge3J9CnByaW50KGthYmxlKG1hdGNoUmVzKSkKYGBgCgoKYGBge3J9CmNvZWZUYWJsZS5hcHBlbmQgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KDE6bGVuZ3RoKG1hcHBlbmQpLCBmdW5jdGlvbihtb2RlbCl7CiAgYmV0YSA9IHJvdW5kKG1hcHBlbmRbW21vZGVsXV1bWzFdXSRlc3Qubm9hZGosMykKICBtZWFuLlRyID0gcm91bmQobWFwcGVuZFtbbW9kZWxdXVtbMl1dJEFmdGVyTWF0Y2hpbmdbWzFdXVtbM11dLCAyKQogIG1lYW4uQ28gPSByb3VuZChtYXBwZW5kW1ttb2RlbF1dW1syXV0kQWZ0ZXJNYXRjaGluZ1tbMV1dW1s0XV0sIDIpCiAgcHZhbCA9IG1hcHBlbmRbW21vZGVsXV1bWzJdXSRBZnRlck1hdGNoaW5nW1sxXV1bWzEwXV1bWzNdXSAjIHAudmFsdWUKICAjcHZhbCA9ICgxIC0gcG5vcm0oYWJzKG1bW21vZGVsXV1bWzFdXSRlc3QvbVtbbW9kZWxdXVtbMV1dJHNlLnN0YW5kYXJkKSkpICogMgogIHB2YWwgPSBpZmVsc2UocHZhbCA8IDAuMDAxLCAiMC4wMDEiLCByb3VuZChwdmFsLCAzKSkKICAKICByZXMgPSBkYXRhLmZyYW1lKGJldGEsIG1lYW4uVHIsIG1lYW4uQ28sIHB2YWwpCiAgcmV0dXJuKHJlcykKfSkpCnJvdy5uYW1lcyhjb2VmVGFibGUuYXBwZW5kKSA8LSBuYW1lc0lucHV0CmNvZWZUYWJsZS5hcHBlbmQkcHZhbC5hZGogPC0gcm91bmQocC5hZGp1c3QoY29lZlRhYmxlLmFwcGVuZCRwdmFsLCBtZXRob2Q9ImZkciIpLDMpCm5hbWVzKGNvZWZUYWJsZS5hcHBlbmQpIDwtIGMoImJldGEiLCAiT2xkIENsaWVudHMiLCAiTmV3IENsaWVudHMiLCAicHZhbCIsICJwdmFsLmFkaiIpCmBgYAoKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpwcmludChrYWJsZShjb2VmVGFibGUuYXBwZW5kKSkKYGBgCgpgYGB7cn0KIyBzb3J0IGJ5IHRoZSBvcmRlciBFcmljIHdhbnRzCnQxb3JkZXIuYSA8LSBjKCJwSCIsICJUb3RhbC5DIiwgIlRvdGFsLk4iLCAibTMuQ2EiLCAibTMuTWciLCAiRXhBbCIpCnRhYmxlMXZhcnMuYSA8LSBwYXN0ZSh0MW9yZGVyLmEsIGNvbGxhcHNlID0gInwiKQp0YWJsZTEuYSA8LSBjb2VmVGFibGUuYXBwZW5kW2dyZXAodGFibGUxdmFycy5hLCByb3duYW1lcyhjb2VmVGFibGUuYXBwZW5kKSksIF0KdGFibGUxLmEgPC0gdGFibGUxLmFbb3JkZXIobWF0Y2gocm93bmFtZXModGFibGUxLmEpLCB0MW9yZGVyLmEpKSxdCgp3cml0ZS5jc3YodGFibGUxLmEsIGZpbGU9cGFzdGUoIm91dHB1dCIsICJwc20gY29lZmZpY2llbnRzIG9yZGVyZWQgZm9yIEVTLWFwcGVuZGl4LmNzdiIsIHNlcCA9ICIvIikpCgojIDExLzE3IGFkZGVkIGxpbWUKdDJvcmRlci5hIDwtIGMoIm5fc2Vhc29uX2ZlcnQiLCAibl9zZWFzb25fY29tcG9zdCIsICJuX3NlYXNvbl9saW1lIiwgIm5fc2Vhc29uX2ZhbGxvdyIsICJuX3NlYXNvbnNfbGVnXzEiLCAibl9zZWFzb25zX2xlZ18yIikKdGFibGUydmFycy5hIDwtIHBhc3RlKHQyb3JkZXIuYSwgY29sbGFwc2UgPSAifCIpCnRhYmxlMi5hIDwtIGNvZWZUYWJsZS5hcHBlbmRbZ3JlcCh0YWJsZTJ2YXJzLmEsIHJvd25hbWVzKGNvZWZUYWJsZS5hcHBlbmQpKSwgXQp0YWJsZTIuYSA8LSB0YWJsZTIuYVtvcmRlcihtYXRjaChyb3duYW1lcyh0YWJsZTIuYSksIHQyb3JkZXIuYSkpLF0KCndyaXRlLmNzdih0YWJsZTIuYSwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInBzbSBjb2VmZmljaWVudHMgb3JkZXJlZCBmb3IgRVNfYWdwcmFjLWFwcGVuZGl4LmNzdiIsIHNlcCA9ICIvIikpCgojdGFibGUgMwp0M29yZGVyLmEgPC0gYygiZmVydDEuYXJlIiwgImNvbXBvc3QuYXJlIikKdGFibGUzdmFycy5hIDwtIHBhc3RlKHQzb3JkZXIuYSwgY29sbGFwc2UgPSAifCIpCnRhYmxlMy5hIDwtIGNvZWZUYWJsZS5hcHBlbmRbZ3JlcCh0YWJsZTN2YXJzLmEsIHJvd25hbWVzKGNvZWZUYWJsZS5hcHBlbmQpKSwgXQp0YWJsZTMuYSA8LSB0YWJsZTMuYVtvcmRlcihtYXRjaChyb3duYW1lcyh0YWJsZTMuYSksIHQzb3JkZXIuYSkpLF0Kd3JpdGUuY3N2KHRhYmxlMy5hLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicHNtIGNvZWZmaWNpZW50cyB0YWJsZTMtYXBwZW5kaXguY3N2Iiwgc2VwID0gIi8iKSkKYGBgCgojIyBTdHVkeSBncm91cCBiYWxhbmNlIHBvc3QtbWF0Y2gKClNlZSBbaGVyZV0oaHR0cDovL3N0YXRzLnN0YWNrZXhjaGFuZ2UuY29tL3F1ZXN0aW9ucy81MDAxMy9yZWNvbnN0cnVjdGluZy1tYXRjaGVkLWRhdGEtc2V0LWZyb20tcnMtbWF0Y2hpbmctcGFja2FnZSkgZm9yIHNvbWUgZ3VpZGFuY2Ugb24gaHdvIHRvIHVzZSB3ZWlnaHRzIHRvIHJlY29uc3RydWN0IHRoZSBncm91cCBiYWxhbmNlIGZvbGxvd2luZyB0aGUgbWF0Y2hlcy4KClNlZSBbaGVyZV0oaHR0cDovL2ZpbnppLnBzeWNoLnVwZW5uLmVkdS9saWJyYXJ5L3dlaWdodHMvaHRtbC93dGQudC50ZXN0Lmh0bWwpIGZvciB3ZWlnaHRlZCB0LnRlc3QgZG9jdW1lbnRhdGlvbgoKYGBge3J9CnN1cHByZXNzTWVzc2FnZXMobGlicmFyeSh3ZWlnaHRzKSkKdGFibGVWYXJzIDwtIGMoImFnZSIsICJmZW1hbGUiLCAiaGhzaXplIiwgIm93biIpCgpwb3N0TWF0Y2ggPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KDE6bGVuZ3RoKG0pLCBmdW5jdGlvbihtb2RlbCl7CiAgCiAgaW5uZXJQb3N0IDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseSh0YWJsZVZhcnMsIGZ1bmN0aW9uKHgpewogIAogIG1lYW4udCA9IHdlaWdodGVkLm1lYW4oZFttW1ttb2RlbF1dW1sxXV0kaW5kZXgudHJlYXRlZCxdWyx4XSwgbVtbbW9kZWxdXVtbMV1dJHdlaWdodHMpCiAgbWVhbi5jID0gd2VpZ2h0ZWQubWVhbihkW21bW21vZGVsXV1bWzFdXSRpbmRleC5jb250cm9sLF1bLHhdLCBtW1ttb2RlbF1dW1sxXV0kd2VpZ2h0cykKICAKICAjIGNvbWJpbmVkIGRhdGEKICBkbSA8LSBhcy5kYXRhLmZyYW1lKHJiaW5kKGRbbVtbbW9kZWxdXVtbMV1dJGluZGV4LnRyZWF0ZWQsXSwgCiAgICAgICAgICAgICAgZFttW1ttb2RlbF1dW1sxXV0kaW5kZXguY29udHJvbCxdKSkKICAKICB0ZXN0ID0gd3RkLnQudGVzdChkW21bW21vZGVsXV1bWzFdXSRpbmRleC50cmVhdGVkLF1bLHhdLCAKICAgICAgICAgICAgICAgICAgICBkW21bW21vZGVsXV1bWzFdXSRpbmRleC5jb250cm9sLF1bLHhdLAogICAgICAgICAgICAgICAgICAgIHdlaWdodD1tW1ttb2RlbF1dW1sxXV0kd2VpZ2h0cywKICAgICAgICAgICAgICAgICAgICBzYW1lZGF0YT1UUlVFKQogIAogIHJldHVybihkYXRhLmZyYW1lKG1vZGVsLm51bSA9IG1vZGVsLCAKICAgICAgICAgICAgICAgICAgICBvdXRjb21lPXgsIAogICAgICAgICAgICAgICAgICAgIHRyPW1lYW4udCwgCiAgICAgICAgICAgICAgICAgICAgY29udHIgPSBtZWFuLmMsCiAgICAgICAgICAgICAgICAgICAgcHZhbCA9IHRlc3QkY29lZmZpY2llbnRzWzNdW1sxXV0pKQogIH0pKQogIAogIHJldHVybihpbm5lclBvc3QpCn0pKQpgYGAKCmBgYHtyfQp3cml0ZS5jc3YocG9zdE1hdGNoLCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicncgcG9zdCBtYXRjaCBjb3ZhcnMuY3N2Iiwgc2VwID0gIi8iKSwKICAgICAgICAgIHJvdy5uYW1lcyA9IEYpCmBgYAoKCiMjIE1vZGVscyB3aXRoIFBTTSBtYXRjaGVkIGZhcm1lcnMKClBlciBSb2JlcnQncyBzdWdnZXN0aW9uLCBub3cgdGhhdCB3ZSd2ZSBtYXRjaGVkIFR1YnVyYSBhbmQgbm9uLVR1YnVyYSBmYXJtZXJzLCBsZXQncyBhc3Nlc3MgdGhlIHNldmVyaXR5IG9mIFR1YnVyYSB0ZW51cmUgb24ga2V5IHNvaWwgaGVhbHRoIG91dGNvbWVzLgoKYGBge3J9CnRlbnVyZVRhYi5hZGQgPC0gbGFwcGx5KDE6bGVuZ3RoKG0pLCBmdW5jdGlvbihtb2RlbCl7CiAgCiAgZG0gPC0gYXMuZGF0YS5mcmFtZShyYmluZChkW21bW21vZGVsXV1bWzFdXSRpbmRleC50cmVhdGVkLF0sIAogICAgICAgICAgICAgIGRbbVtbbW9kZWxdXVtbMV1dJGluZGV4LmNvbnRyb2wsXSkpCiAgZG0kY2xpZW50X3RlbnVyZSA8LSBkbSRjbGllbnQqZG0kdG90YWwuc2Vhc29ucwogIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkbVsscHNtTGlzdFtbbW9kZWxdXSR5XSB+IiwgInRvdGFsLnNlYXNvbnMgKyBhcy5mYWN0b3IoY2VsbCkiLCBzZXAgPSIiKSksIGRhdGE9ZG0pCiAgcmV0dXJuKG1vZCkKfSkKCiMgYWRkIHRlbnVyZWQ+PTMgdG8gZCBpZiB3ZSB3YW50IHRvIHJ1biB0aGlzIGFzIGEgYmluYXJ5IGNvbXBhcmlzb24KCiMgdGVudXJlVGFiLmJpbmFyeSA8LSBsYXBwbHkoMTpsZW5ndGgobSksIGZ1bmN0aW9uKG1vZGVsKXsKIyAgIAojICAgZG0gPC0gYXMuZGF0YS5mcmFtZShyYmluZChkW21bW21vZGVsXV1bWzFdXSRpbmRleC50cmVhdGVkLF0sIAojICAgICAgICAgICAgICAgZFttW1ttb2RlbF1dW1sxXV0kaW5kZXguY29udHJvbCxdKSkKIyAgIGRtJGNsaWVudF90ZW51cmUgPC0gZG0kY2xpZW50KmRtJHRvdGFsLnNlYXNvbnMKIyAgIG1vZCA8LSBsbShhcy5mb3JtdWxhKHBhc3RlKCJkbVsscHNtTGlzdFtbbW9kZWxdXSR5XSB+IiwgInRlbnVyZWQgKyBhcy5mYWN0b3IoY2VsbCkiLCBzZXAgPSIiKSksIGRhdGE9ZG0pCiMgICByZXR1cm4obW9kKQojIH0pCgoKYGBgCgpgYGB7ciwgcmVzdWx0cz0nYXNpcyd9CiMgbW9kTmFtZXMgPC0gYygiQ2FsY2l1bSIsICJNYWduZXNpdW0iLCAicEgiLCAiTml0cm9nZW4iLCAiQ2FyYm9uIiwgIlNlYXNvbnMgZmVydGlsaXplciIsICJTZWFzb25zIGNvbXBvc3QiLCAiU2Vhc29ucyBsZWd1bWVzIiwgIlNlYXNvbnMgZmFsbG93IiwgIkZlcnRpbGl6ZXIgKGxvZykiLCAiU2Vhc29uIFNlYy4gbGVndW1lcyIsICJTZWFzb25zIExpbWUiLCAiRmVydGlsaXplci9BcmUiLCAiQ29tcG9zdC9hcmUiKQoKbW9kTmFtZXMgPC0gdW5saXN0KGxhcHBseShwc21MaXN0LCBmdW5jdGlvbih4KXsKICByZXR1cm4oeCR5KQp9KSkKYGBgCgpgYGB7ciBldmFsPUZBTFNFfQpzdXBwcmVzc1dhcm5pbmdzKApzdGFyZ2F6ZXIodGVudXJlVGFiLmFkZCwgdHlwZT0iaHRtbCIsIAogICAgICAgICAgdGl0bGUgPSAiMjAxNkEgUndhbmRhIFNvaWwgSGVhbHRoIEJhc2VsaW5lIC0gUFNNIFRlbnVyZSIsCiAgICAgICAgICBjb3ZhcmlhdGUubGFiZWxzID0gIk9uZSBBY3JlIEZ1bmQgVGVudXJlIiwgCiAgICAgICAgICBkZXAudmFyLmxhYmVscyA9ICIiLAogICAgICAgICAgI2NvbHVtbi5sYWJlbHMgPSBtb2ROYW1lcywKICAgICAgICAgICNjb2x1bW4ubGFiZWxzID0gYyhnc3ViKCJtMy4iLCAiIiwgYXMudmVjdG9yKG5hbWVzSW5wdXQpKSksCiAgICAgICAgICBub3RlcyA9ICJJbmNsdWRlcyBGRSBmb3IgY2VsbCIsCiAgICAgICAgICBvbWl0PWMoImNlbGwiKSwgb3V0PXBhc3RlKG9kLCAicndfYmFzZWxpbmVfbWF0Y2hlZF90ZW51cmUuaHRtIiwgc2VwPSIvIikpCikKYGBgCgpgYGB7cn0KcGxtLnRlbnVyZSA8LSBmdW5jdGlvbih4KXsKICAKICBpbnRlcmNlcHQgPSB4JGNvZWZmaWNpZW50c1tbMV1dCiAgYmV0YSA9IHJvdW5kKHgkY29lZmZpY2llbnRzW1syXV0sMykKICBpbnQucHZhbCA9IHN1bW1hcnkoeCkkY29lZmZpY2llbnRzWzEsNF0KICBpbnQucHZhbCA9IGlmZWxzZShpbnQucHZhbCA8IDAuMDAxLCAiPCAwLjAwMSIsIHJvdW5kKGFzLm51bWVyaWMoaW50LnB2YWwpLDMpKQogIGJldGEucHZhbCA9IHN1bW1hcnkoeCkkY29lZmZpY2llbnRzWzIsNF0KICBiZXRhLnB2YWwgPSBpZmVsc2UoYmV0YS5wdmFsIDwgMC4wMDEsICI8IDAuMDAxIiwgcm91bmQoYXMubnVtZXJpYyhiZXRhLnB2YWwpLDMpKQogIHJlcyA9IGRhdGEuZnJhbWUoaW50ZXJjZXB0LCBpbnQucHZhbCwgYmV0YSwgYmV0YS5wdmFsLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICByZXR1cm4ocmVzKSAgCn0KCnRlbnVyZS5yZWcgPC0gZG8uY2FsbChyYmluZCwgbGFwcGx5KHRlbnVyZVRhYi5hZGQsIGZ1bmN0aW9uKHgpewogIHBsbS50ZW51cmUoeCkKfSkpCgpyb3duYW1lcyh0ZW51cmUucmVnKSA8LSBtb2ROYW1lcwpgYGAKCmBgYHtyfQp0Nm9yZGVyIDwtIGMoImNvd3MiLCAicGlncyIsICJzaGVlcCIsICJnb2F0cyIsICJjaGlja2VucyIpCnRhYmxlNnZhcnMgPC0gcGFzdGUodDZvcmRlciwgY29sbGFwc2UgPSAifCIpCnJ3LnRhYmxlNiA8LSBvdXRwdXRbZ3JlcCh0YWJsZTZ2YXJzLCByb3duYW1lcyhvdXRwdXQpKSwgXQpydy50YWJsZTYgPC0gcncudGFibGU2W29yZGVyKG1hdGNoKHJvd25hbWVzKHJ3LnRhYmxlNiksIHQ0b3JkZXIpKSxdCgpFU29yZGVyIDwtIGMoInBIIiwgIlRvdGFsLkMiLCAiVG90YWwuTiIsICJtMy5DYSIsICJtMy5NZyIsICJFeEFsIiwgIm5fc2Vhc29uX2ZlcnQiLAogICAgICAgICAgICAgImxvZ0ZlcnQiLCAibl9zZWFzb25fY29tcG9zdCIsICJuX3NlYXNvbnNfbGVnXzEiLCAibl9zZWFzb25fZmFsbG93IikKRVNvcmRlci5ncmVwIDwtIHBhc3RlKEVTb3JkZXIsIGNvbGxhcHNlID0gInwiKQp0ZW51cmUucmVnIDwtIHRlbnVyZS5yZWdbZ3JlcChFU29yZGVyLmdyZXAscm93bmFtZXModGVudXJlLnJlZykpLF0KdGVudXJlLnJlZyA8LSB0ZW51cmUucmVnW29yZGVyKG1hdGNoKHJvdy5uYW1lcyh0ZW51cmUucmVnKSwgRVNvcmRlcikpLF0KCndyaXRlLmNzdih0ZW51cmUucmVnLCBmaWxlPXBhc3RlKG9kLCAidGFibGUxM19yZWcuY3N2Iiwgc2VwID0gIi8iKSwgcm93Lm5hbWVzPVQpCmBgYAoKKipJbnRlcnByZXRhdGlvbioqOiBVc2luZyBhIFBTTSBtYXRjaGVkIHNhbXBsZSwgdGhlIG1vZGVscyBhYm92ZSBhc3Nlc3MgdGhlIGVmZmVjdHMgb2YgYWRkaXRpb25hbCB5ZWFycyBvZiBmYXJtaW5nIHdpdGggVHVidXJhLiBOdW1lcm91cyBjb250cm9sIGZhcm1lcnMgaGF2ZSBhbHNvIGJlZW4gVHVidXJhIGZhcm1lcnMgaW4gcHJldmlvdXMgc2Vhc29ucy4gVGh1cywgSSdtIGtlZXBpbmcgdGhlIG1vZGVsIHNpbXBsZSBpbnN0ZWFkIG9mIGFkZGluZyBhIGNsaWVudCp0ZW51cmUgaW50ZXJhY3Rpb24uIFdlIGNhbiBlYXNpbHkgdGVzdCB0aGF0IGFzIHdlbGwgdGhvdWdoLgoKIyBTb2lsIENsdXN0ZXJzCgojIyBIZWlyYXJjaGljYWwgQ2x1c3RlcmluZwpJIGRyYXcgZnJvbSBbUm9iZXJ0J3NdKHJvYmVydC5vbkBvbmVhY3JlZnVuZC5vcmcpIGV4YW1wbGUgaW4gdGhlIEJHTVMgc29pbCByZXNwb25zZSBub3RlYm9vay4KClNjYWxlIHNvaWwgdmFyaWFibGVzIHRvIHJlbW92ZSB1bml0cyBhcyBhbiBpc3N1ZS4KCmBgYHtyfQpzY2FsZWRTb2lsIDwtIHNjYWxlKGRbLHNvaWxWYXJzXSkKYGBgCgpgYGB7cn0KY2x1c3RNZXRob2RzIDwtIGMoIndhcmQuRDIiLCAic2luZ2xlIiwgImNvbXBsZXRlIiwgImF2ZXJhZ2UiLCAibWNxdWl0dHkiLCAibWVkaWFuIiwgImNlbnRyb2lkIikKaW52aXNpYmxlKHNhcHBseShjbHVzdE1ldGhvZHMsIEZVTj1mdW5jdGlvbihtZXRob2QpIHsKICAgIHBsb3QoaGNsdXN0KGRpc3Qoc2NhbGVkU29pbCksIG1ldGhvZD1tZXRob2QpLCBtYWluPW1ldGhvZCwgeWxhYj0iIiwgbGFiZWw9RkFMU0UpCn0pKQpgYGAKCioqTm90ZSoqOiBJJ20gaG9uZXN0bHkgbm90IGNlcnRhaW4gd2hhdCB3ZSdyZSBsb29raW5nIGZvciBpbiB0aGVzZSBvdXRwdXRzLiBMb29rIGZvciBhIGJhbGFuY2VkIHRyZWU/IFdhcmQgbG9va3MgcHJldHR5IGJhbGFuY2VkLgoKYGBge3J9CmhjIDwtIGhjbHVzdChkaXN0KHNjYWxlZFNvaWwpLCBtZXRob2Q9IndhcmQuRDIiKQpgYGAKCmBgYHtyfQpkJGhjNCA8LSBjdXRyZWUoaGMsIGs9NCkKaGNDZW50cm9pZHMgPC0gYWdncmVnYXRlKGxpc3QoZFssc29pbFZhcnNdKSwgYnk9bGlzdChjbHVzdGVyPWQkaGM0KSwgRlVOPW1lYW4pCmhjVGFibGUgPC0gcmJpbmQodCh0YWJsZShkJGhjNCkpLAogICAgICB0KHJvdW5kKGhjQ2VudHJvaWRzLCAyKSkpCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnByaW50KGthYmxlKGhjVGFibGUpKQpgYGAKCiMjIEstbWVhbnMgY2x1c3RlcmluZwoKSy1tZWFucyBjbHVzdGVyaW5nIGlzIGEgdHlwaWNhbCB1bnN1cGVydmlzZWQgbGVhcm5pbmcgYWxnb3JpdGhtLiBXZSBzdGFydCBieSBpZGVudGlmeWluZyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHdlIHNob3VsZCB0ZWxsIHRoZSBmb3JtdWxhIHRvIGxvb2sgZm9yLgoKYGBge3J9Cm51bUNsdXN0ZXJzIDwtIDI6MTAKcGxvdChudW1DbHVzdGVycywKICAgICBzYXBwbHkobnVtQ2x1c3RlcnMsIEZVTj1mdW5jdGlvbihrKSB7IHN1bShrbWVhbnMoc2NhbGVkU29pbCwgY2VudGVycz1rLCBuc3RhcnQ9MjAsIGl0ZXIubWF4PTIwKSR3aXRoaW5zcykgfSksCiAgICAgdHlwZT0iYiIsIHlsYWI9ImF2ZXJhZ2Ugd2l0aGluIGNsdXN0ZXIgc3VtIG9mIHNxdWFyZWQgZXJyb3IiKQpgYGAKCldlJ3JlIGxvb2tpbmcgZm9yIHRoZSBiZW5kIGluIHRoZSBncmFwaC4gSXQgc2VlbXMgdG8gY29tZSBhdCAzIGJ1dCB3ZSBjYW4gdHJ5IGJvdGggMyBhbmQgNCBhbmQgc2VlIGhvdyB0aGV5IG1hdGNoIHdpdGggb3VyIHVuZGVyc3RhbmRpbmcgb2YgQUVaIGFuZCBlbnZpcm9ubWVudGFsIGNvbmRpdGlvbnMuCgpgYGB7cn0Kc2V0LnNlZWQoMjAxNjExMjUpCmNsdXN0ZXJzIDwtIDQKZCRrbTQgPC0ga21lYW5zKHNjYWxlZFNvaWwsIGNlbnRlcnM9Y2x1c3RlcnMsIG5zdGFydD0yMCwgaXRlci5tYXg9MjApJGNsdXN0ZXIKa21DZW50cm9pZHMgPC0gYWdncmVnYXRlKGxpc3QoZFssc29pbFZhcnNdKSwgYnk9bGlzdChjbHVzdGVyPWQka200KSwgRlVOPW1lYW4pCmttVGFibGUgPC0gcmJpbmQodCh0YWJsZShkJGttNCkpLAogICAgICB0KHJvdW5kKGttQ2VudHJvaWRzLCAyKSkpCmBgYAoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnByaW50KGthYmxlKGttVGFibGUpKQpgYGAKClRoZXNlIGNsdXN0ZXJzIGFyZSByb3VnaGx5IGVxdWFsbHkgc2l6ZWQuIFJvYmVydCB1c2VkIGFub3RoZXIgbWV0aG9kLCBwYXJ0aXRpb25pbmcgYXJvdW5kIE1lZGlvZHMsIHRvIGJldHRlciBhY2NvdW50IGZvciBvdXRsaWVycyB0aGF0IHdlcmUgY2F1c2luZyB0aGUgYWxnb3JpdGhtIHRvIGNyZWF0ZSBhIHNtYWxsIGFkZGl0aW9uYWwgY2x1c3Rlci4gCgpHaXZlIHRoZSBjbHVzdGVycyBicmllZiBkZXNjcmlwdGlvbnMKYGBge3J9CmQka200IDwtIGZhY3RvcihkJGttNCwgbGFiZWxzPWMoInBIPTUuOSwgQz0xLjciLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicEg9Ni4xLEM9Mi40IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBIPTUuMSwgQz0yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicEg9NC44LCBDPTIuNiIpKQpgYGAKCgojIyBNYXBwYWluZyB0aGUgbG9jYXRpb24gb2YgdGhlIGNsdXN0ZXJzCmBgYHtyfQpzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2dtYXApKQpxbXBsb3QobG9uLCBsYXQsIGRhdGE9ZCwgY29sb3I9ZCRrbTQpCiNxbXBsb3QobG9uLCBsYXQsIGRhdGE9ZCwgY29sb3I9YXMuZmFjdG9yKGhjNCkpCmBgYAoKYGBge3J9CmNyZHJlZiA8LSBDUlMoJytwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0ICtub19kZWZzICtlbGxwcz1XR1M4NCArdG93Z3M4ND0wLDAsMCcpCmUgPC0gZFshaXMubmEoZCRsb24pLF0Kc3MgPC0gU3BhdGlhbFBvaW50c0RhdGFGcmFtZShjb29yZHMgPSBlWywgYygibG9uIiwgImxhdCIpXSwgZGF0YT1lLCBwcm9qNHN0cmluZyA9IGNyZHJlZikKCnBhbCA8LSBjb2xvckZhY3RvcigiUmRZbEJ1IiwgZG9tYWluPXVuaXF1ZShzcyRrbTQpKQoKbGVhZmxldCgpICU+JSBhZGRUaWxlcygpICU+JQogIHNldFZpZXcobG5nPXJ3YW5kYSRsb25naXR1ZGUsIGxhdD1yd2FuZGEkbGF0aXR1ZGUsIHpvb209OCkgJT4lCiAgYWRkQ2lyY2xlTWFya2Vycyhsbmc9c3MkbG9uLCBsYXQ9c3MkbGF0LCAKICAgICAgICAgICAgICAgICAgICNyYWRpdXM9IGlmZWxzZShzcyRrbTQ9PTEsIDEwLDYpLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBwYWwoc3Mka200KSwgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucyhkaXNhYmxlQ2x1c3RlcmluZ0F0Wm9vbT0xMSwgc3BpZGVyZnlPbk1heFpvb209RkFMU0UpKSAlPiUgCiAgYWRkTGVnZW5kKHBhbCA9IHBhbCwgdmFsdWVzPXVuaXF1ZShzcyRrbTQpLCB0aXRsZSA9ICJDbHVzdGVycyAiKQogIApgYGAKCiMjIFNvaWwgY2x1c3RlcnMgYnkgYWx0aXR1ZGUgYW5kIEFFWgpgYGB7cn0KZ2dwbG90KGQsIGFlcyh4PWttNCwgeT1hbHQpKSArIGdlb21fYm94cGxvdCgpCmBgYAoKIyMjIFNvaWwgY2x1c3RlcnMgYnkgYWx0aXR1ZGUgYW5kIHJhaW5mYWxsClRyeSB0aGlzIHdoZW4gd2UgaGF2ZSBhV2hlcmUgZGF0YQoKIyMgQ29tcGFyZSBjbHVzdGVycyB0byBBRVoKYGBge3IgcmVzdWx0cz0nYXNpcyd9CnByaW50KGthYmxlKHRhYmxlKGQka200LCBkJGFleikpKQpgYGAKCldlJ3JlIG5vdCBzZWVpbmcgY2xvc2UgYWxpZ25tZW50IGJldHdlZW4gY2x1c3RlcnMgYW5kIEFFWi4gVGhpcyBpcyBwcm9iYWJseSBiZWNhdXNlIEFFWiBhcmUgYSBtaXggb2YgYWx0aXR1ZGUsIHJhaW5mYWxsLCB0ZW1wZXJhdHVyZSBhbmQgc29pbC4gV2hlbiB3ZSBoYXZlIHdlYXRoZXIgZGF0YSB3ZSBjYW4gdHJ5IHRvIGNsdXN0ZXIgYWdhaW4gYW5kIHNlZSBpZiB3ZSdyZSBtb3JlIGNsb3NlbHkgYWxpZ25lZC4KCkFsdGVybmF0aXZlbHksIHRoaXMgY291bGQgYmUgaW5pdGlhbCBldmlkZW5jZSB0aGF0IHRoZSBBRVogZGVzaWduYXRpb25zIGFyZSBub3QgY2FwdHVyaW5nIGFuZCBzZXBhcmF0aW5nIHZhcmlhdGlvbiB2ZXJ5IHdlbGwuIEkgdGhpbmsgd2UgbmVlZCB0byBzdGFydCB3aXRoIG1vcmUgZGF0YSB0aG91Z2guCgojIE1hcHBpbmcKCiMjIFNvaWwgaGVhbHRoIG1hcHMgZm9yIHNvaWwgaGVhbHRoIHN0dWR5IGFyZWFzCgpgYGB7cn0KI2NyZHJlZiA8LSBDUlMoJytwcm9qPWxvbmdsYXQgK2RhdHVtPVdHUzg0JykKY3JkcmVmIDwtIENSUygnK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK25vX2RlZnMgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwJykKZSA8LSBkWyFpcy5uYShkJGxvbiksXQpzcyA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKGNvb3JkcyA9IGVbLCBjKCJsb24iLCAibGF0IildLCBkYXRhPWUsIHByb2o0c3RyaW5nID0gY3JkcmVmKQoKcncgPC0gZ2V0RGF0YSgiR0FETSIsIGNvdW50cnk9J1JXJywgbGV2ZWw9MywgcGF0aCA9ICIvVXNlcnMvbWxvd2VzL2RyaXZlL29wdGltaXplZF9hZ3Jvbm9teS9zb2lsL3NvaWxfaGVhbHRoX3N0dWR5L2RhdGEiKSAjIHRoZSBoaWdoZXIgdGhlIG51bWJlciwgdGhlIGhpZ2hlciB0aGUgcmVzb2x1dGlvbgoKI2V4dC5ydy5zcyBtYXRjaGVzIHBvaW50cyB3aXRoIHNwYXRpYWwgcG9seWdvbnMgaW4gcncKZXh0LnJ3LnNzIDwtIGV4dHJhY3QocndbLCAiTkFNRV8zIl0sIHNzKQpzcyRzcGF0aWFsbmFtZSA8LSBleHQucncuc3MkTkFNRV8zCmZydyA8LSBmb3J0aWZ5KHJ3LCByZWdpb249Ik5BTUVfMyIpCgojZm9yIHNvaWwgZmVhdHVyZXMKc3Muc29pbCA8LSBhZ2dyZWdhdGUoc3NAZGF0YVssc29pbFZhcnNdLCBieT1saXN0KHNzQGRhdGEkc3BhdGlhbG5hbWUpLCBmdW5jdGlvbih4KXsKICBtZWFuKHgsIG5hLnJtPVQpCn0pCgpwbG90UmVhZHkgPC0gZHBseXI6OmxlZnRfam9pbihmcncsIHNzLnNvaWwsIGJ5PWMoImlkIj0iR3JvdXAuMSIpKQoKc3MuY291bnQgPC0gc3NAZGF0YSAlPiUgZ3JvdXBfYnkoc3BhdGlhbG5hbWUpICU+JSBkcGx5cjo6c3VtbWFyaXplKAogIGNvdW50ID0gbigpCikgJT4lIGFzLmRhdGEuZnJhbWUoKQoKY291bnRQbG90IDwtIGRwbHlyOjpsZWZ0X2pvaW4oZnJ3LCBzcy5jb3VudCwgYnk9YygiaWQiPSJzcGF0aWFsbmFtZSIpKQoKCmBgYAoKR2VuZXJhdGUgZGlzdHJpY3QgZGVuc2l0eSBzdW1tYXJpZXMgZm9yIE5hdGhhbmllbCB0byBwYWlyIHdpdGggdGhlIG1hcAoKYGBge3J9CmRpc3RyaWN0RGVuc2l0eSA8LSBkICU+JSBncm91cF9ieShkaXN0cmljdCkgJT4lIGRwbHlyOjpzdW1tYXJpemUoCiAgY291bnQgPSBuKCkKKSAlPiUgYXMuZGF0YS5mcmFtZSgpCgpjZWxsRGVuc2l0eSA8LSBkICU+JSBncm91cF9ieShkaXN0cmljdCwgY2VsbCkgJT4lIGRwbHlyOjpzdW1tYXJpemUoCiAgY291bnQgPSBuKCkKKSAlPiUgYXMuZGF0YS5mcmFtZSgpCgp3cml0ZS5jc3YoZGlzdHJpY3REZW5zaXR5LCBmaWxlPXBhc3RlKCJvdXRwdXQiLCAicncgZGlzdHJpY3QgZGVuc2l0eSB0YWJsZS5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzPVQpCndyaXRlLmNzdihjZWxsRGVuc2l0eSwgZmlsZT1wYXN0ZSgib3V0cHV0IiwgInJ3IGNlbGwgZGVuc2l0eSB0YWJsZS5jc3YiLCBzZXAgPSAiLyIpLAogICAgICAgICAgcm93Lm5hbWVzPVQpCgpgYGAKCgojIyBTdGF0aWMgbWFwIG9mIHNhbXBsaW5nIGRlbnNpdHkKCmBgYHtyfQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbWFwUmVzIDwtICBnZ3Bsb3QoY291bnRQbG90LCBhZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArIGdlb21fcGF0aCgpICsgCiAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsPWNvdW50UGxvdCRjb3VudCkpICsgCiAgI2Nvb3JkX21hcCh4bGltID0gYygzMy41LCAzNikseWxpbSA9IGMoLTIsIDEuNzUpKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGJyZXdlci5wYWwoOSwiUmVkcyIpLCAjIGRlZmluZSBjb2xvcnMKICAgICAgICBuYW1lID0gIlNhbXBsaW5nIERlbnNpdHkiLAogICAgICAgIGd1aWRlID0gZ3VpZGVfY29sb3JiYXIobGVnZW5kLmRpcmVjdGlvbiA9ICJ2ZXJ0aWNhbCIpKSArIAogIHRoZW1lX2J3KCkgKyAKICBsYWJzKHRpdGxlPSJSd2FuZGEgc29pbCBoZWFsdGggc3R1ZHkgc2FtcGxpbmcgZGVuc2l0eSIsIHggPSAiTG9uZ2l0dWRlIiwgeT0iTGF0aXR1ZGUiKQpwcmludChtYXBSZXMpCgpwZGYocGFzdGUob2QsICJyd2FuZGEgc2FtcGxpbmcgZGVuc2l0eS5wZGYiLCBzZXAgPSAiLyIpLCB3aWR0aD0xMSwgaGVpZ2h0PTguNSkKcHJpbnQobWFwUmVzKQpkZXYub2ZmKCkKYGBgCgojIyBQbG90IHNpbXBsZSBzdW1tYXJ5IG9mIHNvaWwgY2hhcmFjdGVyaXN0aWNzCgpDb25zaWRlciByZXZpc2luZyB0aGVzZSBtYXBzIHRvIGEgc21hbGxlciBncmVvZ3JhcGhpYyB1bml0LiBBZGQgdGhlIG5hbWUgb2YgdGhlIGxvY2F0aW9uIGZvciB1bmluaXRpYXRlZCB1c2Vycy4KYGBge3IgY2FjaGU9VFJVRX0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCm1hcExpc3QgPC0gbGlzdCgpCmZvcihpIGluIDE6bGVuZ3RoKHNvaWxWYXJzKSl7Cm1hcFJlcyA8LSAgZ2dwbG90KHBsb3RSZWFkeSwgYWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKyBnZW9tX3BhdGgoKSArIAogIGdlb21fcG9seWdvbihhZXMoZmlsbD1wbG90UmVhZHlbLHNvaWxWYXJzW2ldXSkpICsgCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHJldihicmV3ZXIucGFsKDksIlJlZHMiKSksICMgZGVmaW5lIGNvbG9ycwogICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSBzb2lsVmFyc1tpXSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGxlZ2VuZC5kaXJlY3Rpb24gPSAidmVydGljYWwiKSkgKyAKICB0aGVtZV9idygpICsgCiAgbGFicyh0aXRsZT1wYXN0ZSgiUndhbmRhIGxvbmcgdGVybSBzb2lsIGhlYWx0aCBiYXNlbGluZSAtIDIwMTYiLCBzb2lsVmFyc1tpXSwgc2VwPSAiICIpLCB4ID0gIkxvbmdpdHVkZSIsIHk9IkxhdGl0dWRlIikKbWFwTGlzdFtbaV1dIDwtIG1hcFJlcwpwcmludChtYXBSZXMpCn0gCmBgYAoKYGBge3IgZXZhbD1GQUxTRX0KIyBUaGlzIGlzIGEgc21hbGwgZXhwZXJpbWVudCB0byBjb21iaW5lIHJhc3RlciAoc3BkZikgYW5kIGxlYWZsZXQgYW5kIGJlIGFibGUgdG8gYWNjZXNzIHRoZSBkYXRhIGluIHRoZSByYXN0ZXIgaW50ZXJhY3RpdmVseS4KI21hcExheWVyIDwtIHNwOjptZXJnZShydywgc3Muc29pbCwgYnkueD0iTkFNRV8zIiwgYnkueT0iR3JvdXAuMSIpCgojIGZpbGwgPSBULCBmaWxsT3BhY2l0eSA9IDAuNywgZmlsbENvbG9yID0gZC5maWxsLCAKIyAgICAgICAgIHN0cm9rZSA9IFQsIGNvbG9yID0gIndoaXRlIiwgd2VpZ2h0ID0gMiwgZGFzaEFycmF5ID0gMywgCiMgICAgICAgICBvcGFjaXR5ID0gMC41LCBwb3B1cCA9IGNvdW50eS50dChkKQoKIyBsZWFmbGV0KG1hcExheWVyKSAlPiUgYWRkVGlsZXMoKSAlPiUKIyAgIHNldFZpZXcobG5nPXJ3YW5kYSRsb25naXR1ZGUsIGxhdD1yd2FuZGEkbGF0aXR1ZGUsIHpvb209OCkgJT4lCiMgICBhZGRQb2x5Z29ucygKIyAgICAgc3Ryb2tlID0gVFJVRSwgb3BhY2l0eT0wLjIsIHNtb290aEZhY3RvciA9IDAuNSwgCiMgICAgIGZpbGxDb2xvcj1tYXBMYXllciRwSCwgZmlsbE9wYWNpdHkgPSAwLjUpCgpgYGAKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9CnBkZihmaWxlPXBhc3RlKG1kLCAicndfc2hzX2Jhc2VsaW5lX3NvaWxfbWFwcy5wZGYiLCBzZXAgPSAiLyIpLCB3aWR0aD0xMSwgaGVpZ2h0PTguNSkKZm9yKGkgaW4gMTpsZW5ndGgobWFwTGlzdCkpewpwcmludChtYXBMaXN0W1tpXV0pCn0gIApkZXYub2ZmKCkKYGBgCgoKIyMgSW50ZXJwb2xhdGlvbnMgb2Ygc29pbCBoZWFsdGggdmFsdWVzCkludGVycG9sYXRlIHNvaWwgaGVhbHRoIHZhbHVlcyBmb3IgZnVsbCBvcGVyYXRpbmcgYXJlYSB1c2luZyBzb2lsIGhlYWx0aCBzdHVkeSB2YWx1ZXMuIFdlIHdhbnQgdG8gZXZlbnR1YWxseSBhZGQgYWxsIFJ3YW5kYW4gc29pbCB2YWx1ZXMgaW50byBhIHNpbmdsZSBkYXRhc2V0IHRvIHVwZGF0ZSBhbmQgaG9uZSB0aGVzZSB2YWx1ZXMuIFNlZSBbaGVyZV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0L3Jhc3Rlci5odG1sKSBmb3IgbW9yZSBndWlkYW5hY2UKCioqbm90ZSoqOiAKCiogTGF5ZXIgb24gdGhlIFR1YnVyYSBzaXRlcyB0byB0aGUgaW50ZXJwb2xhdGVkIG1hcC4gCiogVXNlIGxlYWZsZXQgc28geW91IGNhbiB6b29tIGluIGFuZCBvdXQgbW9yZSBlYXNpbHkuIAoqIE1ha2UgaXQgYSByYXN0ZXIgbGF5ZXIgdGhhdCB5b3UgY2FuIGNsaWNrIG9uIHVuZGVyc3RhbmQgdGhlIHZhbHVlcyBhdCBkaWZmZXJlbnQgbG9jYXRpb25zIGFuZCB0aGUgbmFtZSBvZiB0aGUgbG9jYXRpb24uIEknbGwgbmVlZCB0byBtYWtlIHRoaXMgYSBbc2hpbnkgYXBwXShodHRwOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI4OTM4NjQyL21hcmtlci1tb3VzZS1jbGljay1ldmVudC1pbi1yLWxlYWZsZXQtZm9yLXNoaW55KSB0byBoYXZlIHRoYXQgZnVuY3Rpb25hbGl0eQoKVGhlIGNvZGUgYmVsb3cgd2lsbCBydW4gNSBLLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiB0byBjb21wYXJlIGludGVycG9sYXRpb24gbW9kZWxzLiBUaGUgb3V0cHV0IHdpbGwgYmUgZmVkIGludG8gdGhlIGludGVycG9sYXRlIGxlYWZsZXQgY29kZSBiZWxvdy4KCgoqKkNoZWNrIHRoYXQgSSdtIGhhbmRsaW5nIHRoZSBwcm9qZWN0aW9uIGNvcnJlY3RseSB3aXRoIFJvYmVydCoqCgpgYGB7ciBjYWNoZT1GQUxTRX0KIyBwcm9qNHN0cmluZyhzcykgPC0gQ1JTKCIraW5pdD1lcHNnOjQzMjYiKQojIHNzIDwtIHNwVHJhbnNmb3JtKHNzLCBDUlM9KCIrcHJvaj11dG0gK3pvbmU9MzZOICtkYXR1bT1XR1M4NCIpKQpgYGAKCmBgYHtyIHJlc3VsdHM9RkFMU0V9CgojIHJvb3QgbWVhbiBzcSBlcnJvciBmb3IgZXZhbHVhdGluZyBtb2RlbHMKUk1TRSA8LSBmdW5jdGlvbihvYnNlcnZlZCwgcHJlZGljdGVkKSB7CiAgc3FydChtZWFuKChwcmVkaWN0ZWQgLSBvYnNlcnZlZCleMiwgbmEucm09VFJVRSkpCn0KCiMgc2V0IGsgZm9sZHMgdG8gNQpzZXQuc2VlZCgyMDE2MTAzMCkKbmZvbGRzIDwtIDUKayA8LSBrZm9sZChzcywgbmZvbGRzKSAjIGZyb20gZGlzbW8KCiMgY3Jvc3MgdmFsaWRhdGlvbiBvZiBtb2RlbHMKZW5zcm1zZSA8LSB0cHNybXNlIDwtIGlkd3Jtc2UgPC0gcmVwKE5BLCA1KSAjIGFzc2luZyBtdWx0aXBsZSBvYmplY3RzIGF0IG9uY2UKCmN2IDwtIGZ1bmN0aW9uKHgpIHsKICAKICBmb3IoaSBpbiAxOm5mb2xkcykgewogICAgdHJhaW4gPC0gc3NbayE9aSxdCiAgICB0ZXN0IDwtIHNzW2s9PWksXQoKICB0cmFpbiA8LSB0cmFpblshaXMubmEodHJhaW5AZGF0YVsseF0pLF0KICAKICAjIGludmVyc2UgZGlzdGFuY2Ugd2VpZ2h0cwogIG0gPC0gZ3N0YXQoZm9ybXVsYT1hcy5mb3JtdWxhKHBhc3RlKHgsICd+IDEnKSksIGxvY2F0aW9ucz10cmFpbikKICBwMSA8LSBwcmVkaWN0KG0sIG5ld2RhdGE9dGVzdCwgZGVidWcubGV2ZWw9MCkkdmFyMS5wcmVkCiAgaWR3cm1zZVtpXSA8LSAgUk1TRSh0ZXN0QGRhdGFbLHhdLCBwMSkgI2lkdyByc21lCiAgCiAgIyBrcmllZ2luZwogICMgbSA8LSBhdXRvS3JpZ2UoZm9ybXVsYT1hcy5mb3JtdWxhKHBhc3RlKHgsICJ+IDEiKSksIGlucHV0X2RhdGEgPSB0cmFpbikKICAjIHAyIDwtIHByZWRpY3QobSwgbmV3ZGF0YT10ZXN0LCBkZWJ1Zy5sZXZlbD0wKSR2YXIxLnByZWQKICAjIGtyaWdybXNlW2ldIDwtICBSTVNFKHRlc3QkT1pETFlBViwgcDIpCiAgCiAgIyB0aGluIHBsYXRlIHNwbGluZQogIG0gPC0gVHBzKGNvb3JkaW5hdGVzKHRyYWluKSwgdHJhaW5AZGF0YVsseF0pIAogIHAzIDwtIHByZWRpY3QobSwgY29vcmRpbmF0ZXModGVzdCkpCiAgdHBzcm1zZVtpXSA8LSAgUk1TRSh0ZXN0QGRhdGFbLHhdLCBwMykKICAKICB3IDwtIGMoaWR3cm1zZVtpXSwgdHBzcm1zZVtpXSkgIyBjb21iaW5lIHRoZSBybXNlCiAgd2VpZ2h0cyA8LSB3IC8gc3VtKHcpICMgd2VpZ2h0IHRoZW0KICBlbnNlbWJsZSA8LSBwMSAqIHdlaWdodHNbMV0gKyBwMyAqIHdlaWdodHNbMl0gCiAgIyBtdWx0aXBseSBwcmVkaWN0aW9ucyBieSB3ZWlnaHRzCiAgZW5zcm1zZVtpXSA8LSAgUk1TRSh0ZXN0QGRhdGFbLHhdLCBlbnNlbWJsZSkgIyB0cnVseSBhbiBlbnNlbWJsZSByZXN1bHQ/CiAgfQogIAogIG91dHB1dCA8LSByYmluZChpZHdybXNlLCB0cHNybXNlLCBlbnNybXNlKQogIHJldHVybihvdXRwdXQpCiAgCn0KYGBgCgoKTm93IGxvb3Agb3ZlciB0aGUgdmFyaWFibGVzIG9mIGludGVyZXN0IHdoZXJlIHggaXMgdGhlIHNvaWxWYXIgdmFyaWFibGUuIAoKCmBgYHtyIHJlc3VsdHM9RkFMU0V9Cm91dHB1dCA8LSBsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpewogIGluaSA8LSBkYXRhLmZyYW1lKGN2KHgpKQogIGluaSRhdmUgPC0gYXBwbHkoaW5pWywxOjVdLCAxLCBmdW5jdGlvbih5KXttZWFuKHksIG5hLnJtPVQpfSkKICByZXMgPC0gcGFzdGUoIkJlc3QgbW9kZWwgaXMgIiwgcm93Lm5hbWVzKGluaVt3aGljaC5taW4oaW5pJGF2ZSksXSksICBzZXAgPSAiIikKICByZXR1cm4obGlzdChpbmksIHJlcykpCn0pCmBgYAoKIyMgSW50ZXJwb2xhdGVkIHNvaWwgbWFwcwoKYGBge3IgbWVzc2FnZT1GQUxTRX0KciA8LSByYXN0ZXIocmVzPTEvMTIpCnIgPC0gY3JvcChyLCBmbG9vcihleHRlbnQocncpKSkKCm1hcHMgPC0gbGFwcGx5KHNvaWxWYXJzLCBmdW5jdGlvbih4KXsKICBtIDwtIFRwcyhjb29yZGluYXRlcyhzcyksIHNzQGRhdGFbLHhdKQogICMgbWFrZSByYXN0ZXIgbGF5ZXIgd2l0aCBtb2RlbCwgcmFzdGVyIGlzIHJ3YW5kYSBlbXB0eSByYXN0ZXIsIG1vZGVsIGlzIG0KICB0cHMgPC0gaW50ZXJwb2xhdGUociwgbSkKICB0cHMgPC0gY3JvcCh0cHMsIHJ3KQogIHRwcyA8LSByYXN0ZXI6Om1hc2sodHBzLCBydykgIyBjdXRzIHRoZSB0cHMgcmFzdGVyIGRvd24gdG8gdGhlIHJ3IGJvdW5kYXJpZXMKICB4IDwtIGdzdWIoIm0zLiIsICIiLCB4KQogIAogIHBhbENvbG9ycyA8LSBsZWFmbGV0Ojpjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJSZWRzIiwgdmFsdWVzKHRwcyksIG5hLmNvbG9yID0gInRyYW5zcGFyZW50IikKICAKICBzdXBwcmVzc1dhcm5pbmdzKAogIGxlYWZsZXQoKSAlPiUgYWRkVGlsZXMoKSAlPiUKICBhZGRSYXN0ZXJJbWFnZSh0cHMsIGNvbG9ycz1wYWxDb2xvcnMsIG9wYWNpdHkgPSAwLjgpICU+JQogIHNldFZpZXcobG5nPXJ3YW5kYSRsb25naXR1ZGUsIGxhdD1yd2FuZGEkbGF0aXR1ZGUsIHpvb209OCkgJT4lCiAgYWRkTGVnZW5kKHBhbCA9IHBhbENvbG9ycywgdmFsdWVzID0gdmFsdWVzKHRwcyksIHRpdGxlID0gcGFzdGUoIlNvaWwgVmFsdWUgIiwgeCwgc2VwPSIiKSkKICApCn0pCgpzYXZlKG1hcHMsc3MsIGZpbGU9cGFzdGUoZGQsICJyd19iYXNlbGluZV9pbnRlcnBvbGF0aW9uX21hcHMuUmRhdGEiLCBzZXAgPSAiLyIpKQoKdGFnTGlzdChtYXBzKQpgYGAKClByaW50IG91dCB0aGUgaW50ZXJwb2xhdGVkIHZhbHVlcyBmb3IgaW5jbHVzaW9uIGluIHRoZSByZXBvcnQuIAoKYGBge3J9CnBkZihmaWxlPXBhc3RlKG1kLCAicndfc2hzX2JsX2ludGVycG9sYXRpb25fc29pbF92YXJzLnBkZiIsIHNlcCA9ICcvJyksIHdpZHRoPTExLCBoZWlnaHQ9OC41KQpsYXBwbHkoc29pbFZhcnMsIGZ1bmN0aW9uKHgpIHsKICBtIDwtIFRwcyhjb29yZGluYXRlcyhzcyksIHNzQGRhdGFbLHhdKQogICMgbWFrZSByYXN0ZXIgbGF5ZXIgd2l0aCBtb2RlbCwgcmFzdGVyIGlzIHJ3YW5kYSBlbXB0eSByYXN0ZXIsIG1vZGVsIGlzIG0KICB0cHMgPC0gaW50ZXJwb2xhdGUociwgbSkKICB0cHMgPC0gY3JvcCh0cHMsIHJ3KQogIHRwcyA8LSByYXN0ZXI6Om1hc2sodHBzLCBydykgIyBjdXRzIHRoZSB0cHMgcmFzdGVyIGRvd24gdG8gdGhlIHJ3IGJvdW5kYXJpZXMKICB4IDwtIGdzdWIoIm0zLiIsICIiLCB4KQogIApzdXBwcmVzc01lc3NhZ2VzKAogIG91dFBsb3QgPC0gaW52aXNpYmxlKHByaW50KAogIHBsb3QodHBzLCBtYWluPSBwYXN0ZSgiU29pbCBUUFMgSW50ZXJwb2xhdGlvbiAiLCB4LCBzZXA9Ii0gIikpLAogIHBsb3QocncsIGFkZD1ULCBuYS5wcmludD1OVUxMKQogICkpCikKfSkKZGV2Lm9mZigpCmBgYAoKCi0tZW5k